Thursday, December 30, 2010

CRM 2011 Beta :- GetGeoCode activity for the CRM Workflows

With Maps becoming more and more popular way of looking at the data like addresses it has become necessary that we save the addresses of the entities in longitude and latitude format so as to ensure that it can be displayed on the maps easily. 
I have an entity called School in my CRM 2011 Beta system and it has an address associated with it. A few of the attributes of entity are 
1) Name 
2) AddressLine
3) City
4) PostalCode
5) State
6) Country (Defaulted to USA for this discussion) 
7) A few more attributes which are not relevant for this discussion


There's a CRM Form where the user of the system goes and creates the entity instances of the system. The requirement is to show all the schools in the CRM system on a Bing Map. Now this can be achieved using various controls and SDKs like Telerik Controls, Bing Map SDK, Open Map Provider for Telerik Controls and some Google Map APIs as well. But most of these SDKs expect a Longitude and Latitude to be provided for the address so that they can quickly pinpoint the location on their map. 


Longitude and Latitude is something i can't ask the user to fill as many end users might not understand what it means and most won't have any idea about the latitude and longitude on which the school is present on the map. So the easiest way is to achieve it using a plug in or workflow which can run asynchronously to populate the longitude and latitude of the school entity instance. I chose to do it using a workflow process. 
But the question is how would the workflow process determine the longitude and latitude given the Address. The answer to this is that there are various Geocode APIs available. A few popular ones are 
1) Yahoo PlaceFinder API 
2) Google GeoCode API
3) Bing GeoCode Service
4) Bing Locations API


In my case i chose to use Bing Locations API to find the longitude and location of the given address. 


It is a rest based service and can be called by shooting a GET request on the URL in the following format 


http://dev.virtualearth.net/REST/v1/Locations/{CountryCode}/{StateCode}/{PostalCode}/{City}/{StreetAddress}?output=xml&key={BingMapKey} 


You can get a BingMap API access key for free by creating an account on https://www.bingmapsportal.com/ 


The sample CRM Activity code which i wrote can be downloaded from here. You can compile this code into a dll. Sign it with a key and register the same using CRM Plugin Registration Utility which ships with CRM SDK. 


Njoi the APIs. 


~Abhishek



Thursday, December 9, 2010

Insufficient Privileges. The logged-on user does not have the appropriate security permissions to view these records or perform the specific action.

While working on CRM 2011 Beta i created a simple entity model and created a role. On this role i gave read permissions for all the entities in the entity model and then assigned a user to this role. 
After this when i logged on to the CRM system using the web client with the user just created i was assuming that i would get all the entities in my workplace and i would be able to work with them. But turned out i got the above message instead which said. 
"Insufficient Privileges. The logged-on user does not have the appropriate security permissions to view these records or perform the specific action."

I checked the event viewer and found nothing about this message. So the only option was to look into the CRM trace and figure out what is going wrong. 
Here are the steps to enable tracing 

1) Make 3 registry entries on the CRM Server box under "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSCRM"
    a) TraceEnabled of type DWORD 32 bit and set its value to 1
    b) C:\CRMTrace of type string and value as "C:\CRMTrace" . It can be any      folder path
    c) TraceRefresh  of type DWORD 32 bit with any value between 0 and 99

Once this is done the CRM tracing is enabled on the machine. However to my surprise the trace was happening in "C:\Program Files\Microsoft Dynamics CRM\Trace" folder and not in the folder which i mentioned in the registry key.
But without the key the tracing doesn't work so let it be there. 

Once this is done try to login to the CRM system using the same user and you would see a message in the trace which would look something like 

"Crm Exception: Message: Principal user (Id=04becef6-fcf5-df11-96ef-00155d647b03, type=8) is missing prvReadCustomization privilege (Id=7bb3b531-ac45-4977-89c8-b99768e55ab8), ErrorCode: -2147220960"

this basically means that the user is missing a Customization Read privilege in the settings. To fix this we can go in the Setting area as an administrator user and give this privilege to the user or role to which the user belongs. 

Hope this helps in case you are stuck with this kind of problem in 2011 - Beta system. 

Njoi!!!
~Abhishek


Wednesday, December 8, 2010

Getting logged on user details in ItemAdding event in SPItemEventReceiver

I am writing a sharepoint 2010 Custom List event receiver. I am able to get the events which are happening on the list items however there's no obvious way to get the logged on user's details in ItemAdding Event. 
This is the event which occurs before the Item is added. 
SPContext.Current is null for some reason and hence its kind of a dead end to go towards this path. 
A non obvious way to achieve this is by using the following code snippet. 


public override void ItemAdding(SPItemEventProperties properties)
{

      SPUser user =               properties.OpenWeb().AllUsers.GetByID(properties.CurrentUserId)
}

~Abhishek

Saturday, December 4, 2010

CRM 2011 Beta :- Querying linked entities from the system using FetchXml

Many a times there's a requirement on querying the data from entity and joining the data with the linked entity as well. 
e.g. If i have a "Student Event" entity in the CRM system which has a many to one relationship with the "Student" event and if i want to display the "Student Event " details on my UI then obviously i would want to display the "name" attribute of the student on the UI as well. Now if i use RetriveMultiple method to get these details it would take more then one hit on the CRM Server to get all the relevant data. 
To avoid the round trip on the server i can instead use FetchXml method on the CRM Web service. This method would return all the data to me in the form of xml. 
The method takes xml query as input and this xml can be formed using a Stunnware tool which can be downloaded here
Using this tool we can easily create and test the xml which has to be passed to FetchXml method. However there's a catch. If you use this tool to generate the fetch xml it would generate an xml which would look like 




<fetch count="50" mapping="logical" version="1.0">

<entity name="studentevent">

<all-attributes>

<link-entity from="studentid" name="student" to="studentid">

<all-attributes>

</all-attributes></link-entity>

</all-attributes></entity>

</fetch>





and when you execute FetchXml API using this xml you would get all the attributes of the student event entity to be returned however the attributes of the student would not be returned. 
To solve this you need to manually edit the xml and add an alias attribute to the linkentity student. So the final xml should look something like 




<fetch mapping="logical" count="50" version="1.0">

<entity name="studentevent">

<all-attributes />

<link-entity name="student" from="studentid" to="studentid" alias="studentdetails">

<all-attributes />

</link-entity>

</entity>

</fetch>

and this would get you the attributes of studentdetails entity as well.

This may be a bug in the stunnware tool or may be because it was buit to be used against CRM 4.0. Otherwise the tool is an excellent way to generate the xml to query the CRM System with linked entities.

Njoi!!!
~Abhishek

Thursday, December 2, 2010

CRM 2011 Beta :- Creating a Workflow Activity to Generate GUIDs

I am working on CRM 2011 Beta and one of the scenarios I have to create a new entity instance from within a process (Previously known as CRM Workflow.). One of the attributes of this entity is a text field which is supposed to store a GUID and hence by definition it should be unique per instance of the entity. 
I couldn't find a way to generate a GUID on the Process designer. This means i have to write a custom activity which would generate a GUID and pass it on to the next step of creating the entity instance. 
In this post i will capture how to create a custom activity. How to register it with CRM 2011 Beta and how to use it in my Process. I will try to attach appropriate snapshots where necessary. 


Before starting to do anything we have to download the CRM 2011 Beta SDK from here. I extracted the SDK in C:\Temp on my machine however it can be extracted anywhere you want to. 
Of course we will need CRM 2011 Beta to be installed on a machine in order to deploy and test the activity from within a process. We will need Visual Studio 2010 to develop the plug in. (This is not a must as you can use utilities from  .NET SDK to build the code however i am going to use Visual Studio 2010 as i have it installed on my box already). 


First we will create an activity using Visual Studio 


1) Create a New Activity Library type of project in Visual Studio 2010. You can find the template in the Workflow section.


2) Delete the Activity1.xaml File as we are not going to use it. 


3) Add a reference to Microsoft.Xrm.Sdk.dll and Microsoft.Xrm.Workflow.dll which can be found in the bin folder inside the folder where you extracted the CRM SDK.


4) After doing this if you compile the project you might get a warning which would say that the above 2 assemblies refer to the Syste.ServiceModel.Web assembly which is not present in the .NET Framework Client Profile target which is defined for the project. Change the target profile of your project to .NET Framework 4. This can be done by going to properties of your Project and changing the Target Framework. 


5) Create a class called GenerateGuidActivity deriving from CodeActivity class inside System.Activities namespace. Make sure that it is a public sealed class. 


6) To add functionality to the activity we need to override Execute(CodeActivityContext context) method. We will come back to the implementation inside this method. 


7) We do not have any input to this process step as it is just supposed to generate a new Guid everytime. So we do not have any input to the activity. However we have to send the Guid as output of the step. So we will just create a property which needs to return this Guid. Since this would be a output parameter for the Activity we would want to define the property as 
    
public OutArgument NewGuid { get; set; }

Note that i have defined argument as type string and not Guid. This is because CRM doesn't understand Guid as output type.


8) In order to ensure that CRM Process designer can understand this as well. We need to decorate the property with an attribute i.e. [Output("NewGuid")]


9) Once this is done we can go ahead and implement the Execute method. That would be simple. Just create a new Guid and set it with the context.   


this.NewGuid.Set(context, Guid.NewGuid().ToString());


10) Build your code and make sure that the activity compiles. 


11) Since the assembly has to be deployed on a CRM Server it would be a good idea to sign it using a key file and give full trust to the key using caspol. 


Now comes the time to deploy this activity to be accessible by CRM Process Designer. To do that we will use the Plugin Registration Tool which ships with CRM 2011 Beta SDK.  You can find the code for tool in SDK\tools\pluginregistration folder. Open the solution, build and run it. 


12) On the home screen Create a New connection using the menu option. Just enter the server name, user name and a friendly name for the connection. 
Press Connect wait for the connection to complete. Once connected the screen would look like the following. 




13) Now click Register menu and Choose "Register New Assembly" Option. 

14) On the dialog box which opens browse and select the assembly which contains the custom activity we built earlier. 

15) Check the Select All checkbox 

16) Leave the other values to be default i.e. Isolation Mode = None , Location Where assembly  has to be stored = "Database"

17) Click Register Selected Plugins. (PS: You might get a dialog saying that there are no plugins to register. The reason for this may be that the Workflow activity you made earlier is not a public class. You might want to change it to a public class to make this work. Another problem which you might face is an error which says "Public assembly must have public key token." This is happening because you forgot to sign your assembly with a key).

18) Once the registration is successful we can go ahead and give a friendly name to this Workflow Activity using the plugin Registration tool. Just enter the name and press Save. Check the figure below for more clarity. 


19) After this you can go and access this custom workflow activity from the Process designer to be used in your process. 

Happy coding... 

~Abhishek

PS :- Pasting the code of the above exercise....



using System.Activities;
using System;
using Microsoft.Xrm.Sdk.Workflow;

namespace Microsoft.Xrm.CustomWorkflowActivity
{
    
    public sealed class GenerateGuidActivity : CodeActivity
    {
        protected override void Execute(CodeActivityContext context)
        {
            this.NewGuid.Set(context, Guid.NewGuid().ToString());
        }

        [Output("NewGuid")]
        public OutArgument NewGuid { get; set; }
    }
}

Wednesday, December 1, 2010

Problem Accessing Dynamics CRM 2011 Beta services from BCS in Sharepoint 2010

Dynamics 2011 Beta exposes a new oData based service which can be used to pull data out from the system. I am working on BCS for Sharepoint 2010 for a simple demo and i decided to use this service to pull data from CRM 2011 beta system.
After setting the reference i tried to use the DataContext object to pull data out and got an exception which said
"Request version '1.0' is too low for the response. The lowest supported version is '2.0'."
On digging a little deep i realized that some header mismatch is happening between the oData service and my client.
MSDN social says that in order to change this header from 1.0 to 2.0 i need to target my assemblies to compile and run against .NET 4.0. To my utter surprise Sharepoint 2010 runtime can't load the assemblies which are targeted for 4.0 and hence i had to fall back on using the old way of accessing data from CRM which is using CRM data service. That's bad news as this means i will have to write lot of code which i could have done without.
The good thing though is that i will learn the old way of accessing data from the system. Will try to post some code samples.....

Regards, Abhishek