October 2011
Volume 26 Number 10
Silverlight - Harnessing the Power of the Dynamics CRM 4.0 API from Silverlight 4
By Mark Beckner | October 2011
Increasingly, companies are implementing Microsoft Dynamics CRM 4.0 solutions and finding it necessary to build external applications that can integrate with the existing Web service-based API.
Building Silverlight applications that can interact directly with Microsoft Dynamics CRM 4.0 (CRM 4.0 hereafter for brevity) can prove challenging, due to the asynchronous nature of Silverlight calls and its inability to call the CRM 4.0 Web services directly. In this article, you’ll get an in-depth look at how to build a Silverlight application that can read and write data through the CRM 4.0 Web service API.
Solution Overview
Silverlight 4 and CRM 4.0 are both powerful technologies—but they aren’t the most easily integrated. I’ll look at the details behind creating this integration by exploring asynchronous communications between Silverlight and the CRM 4.0 Web service API.
In typical non-Silverlight applications, a call to a Web service is synchronous—the call goes out, and the application waits until a response is received. During this time, the user can’t interact with the application and must wait for the full roundtrip to complete. In an asynchronous application such as Silverlight, the call to the service goes out, but the application continues to be fully functional even before the response is returned. This creates a rich and dynamic user experience, but it places greater demands on the developer.
In order to understand how this communication can occur, I’ll present several pieces of functionality. First, I’ll look at how to set up a Silverlight application and work through the steps required to interact with a Web service that will act as a wrapper for the CRM 4.0 API. Next, I’ll go through the details of working with the CRM 4.0 API and how to read and write data using the Web service wrapper. I’ll work with the core System User entity in CRM 4.0, and also look at working with dynamic entities. Finally, I’ll look at how to deal with the result sets that are returned to Silverlight. In the end, you’ll be able to build your own Silverlight/CRM 4.0 integrations with ease.
Creating the Silverlight 4 Application
There are a number of steps to work through in order to get a Silverlight application configured to interact with CRM 4.0. Although it’s possible to reference the CRM 4.0 SDK or Web service API directly from the Silverlight project, most of the interfaces and methods can’t actually be called from code. In order to interact with the CRM 4.0 API, a wrapper Web service must be created. This Web service will broker the calls between the Silverlight application and the CRM 4.0 API in a format that can be handled by Silverlight. The wrapper Web service can be added directly to the SilverlightCRMDemo.Web application.
Start this process by creating a new Silverlight solution in Visual Studio 2010 called CRM40SilverlightDemo. When creating a Silverlight application in Visual Studio, two projects are always created. One is the core Silverlight application; the second is the ASP.NET application that embeds the Silverlight application into a Web page. This ASP.NET application will also be home to the Web service wrapper that will interact with the CRM 4.0 API. The Silverlight application will reference this Web service via a Service Reference.
To create the Web service wrapper, add a new Web service to the ASP.NET application and call it CrmServiceWrapper. For this example, you’ll add two Web methods to the service—one to get CRM data and one to post CRM data. Figure 1 shows what these stubbed-out methods should look like for now. Once you get the Silverlight application communicating successfully with this wrapper service, you’ll update these methods to call the actual CRM 4.0 API.
Figure 1 The Web Method Stubs
public class CrmServiceWrapper : System.Web.Services.WebService
{
[WebMethod]
public string GetCRMData()
{
return "This is the stubbed return for retrieving";
}
[WebMethod]
public string PostCRMData()
{
return "This is the stubbed return for posting data";
}
}
Once the Web service wrapper has been added to the ASP.NET application, the easiest way to add a reference to it from the Silverlight application is to run it in debug mode and capture the URL where the debugger is running the application from (you can simply grab the URL from the browser window that pops up). Once captured, you can add a new Service Reference called CrmServiceReference to the Silverlight application and paste in this URL. All related configuration files and code will be automatically corrected. If you decide not to do this, you’ll have to deal with cross-domain reference exceptions—and quite a bit more setup in order to successfully debug your application.
Now that the reference exists, the real coding within the Silverlight application can take place. The code consists of wiring up an event handler for each Web method and creating two methods to handle data once the calls to those Web methods have completed. The code shown in Figure 2 can be added directly to the MainPage.xaml.cs file in the Silverlight application. Running this will cause both methods to execute simultaneously.
Figure 2 Adding Code to MainPage.xaml.cs
using CRM40SilverlightDemo.CrmServiceReference;
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
// Call the GetCRMData Web method.
CrmServiceWrapperSoapClient proxyGet =
new CrmServiceWrapperSoapClient();
proxyGet.GetCRMDataCompleted +=
new EventHandler<GetCRMDataCompletedEventArgs>(proxy_GetCRMDataCompleted);
proxyGet.GetCRMDataAsync();
// Call the PostCRMData Web method.
CrmServiceWrapperSoapClient proxyPost = new CrmServiceWrapperSoapClient();
proxyPost.PostCRMDataCompleted +=
new EventHandler<PostCRMDataCompletedEventArgs>(proxy_PostCRMDataCompleted);
proxyPost.PostCRMDataAsync();
}
// Called asynchronously when the GetCRMData Web method returns data.
void proxy_GetCRMDataCompleted(object sender, GetCRMDataCompletedEventArgs e)
{
// Do something with the data returned.
string result = e.Result.ToString();
}
// Called asynchronously when the PostCRMData Web method returns data.
void proxy_PostCRMDataCompleted(object sender, PostCRMDataCompletedEventArgs e)
{
// Do something with the data returned.
string result = e.Result.ToString();
}
}
When you’ve validated that your Silverlight application is running without errors and returning data from the Web service, you can turn your attention to building out the calls to the CRM 4.0 API. These calls will all be contained within the Web service wrapper GetCRMData and PostCRMData Web methods that have already been created.
Interacting with the CRM 4.0 API
There are two primary Web services available through CRM 4.0: the CRMService and the MetadataService. These Web services are generally available to reference from any project (but won’t actually allow for much functionality when referenced from a Silverlight application). The most common and efficient way to work with the API is using the Microsoft Dynamics CRM SDK (available for download at bit.ly/6M3PvV). The SDK contains numerous classes and methods, and it simplifies the communication between .NET Framework code and the CRM 4.0 Web services. In this section, you’ll learn how to interact with the API using the SDK from the Web service wrapper.
The first step is to reference the appropriate CRM SDK assemblies. In the ASP.NET application that houses the Web service wrapper, add a reference to two SDK assemblies: microsoft.crm.sdk.dll and microsoft.crm.sdktypeproxy.dll. Once these have been referenced, add the appropriate directives to the top of the CrmServiceWrapper.asmx page as follows:
using Microsoft.Crm.Sdk;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.Sdk.Query;
The next step is to write code to instantiate a CRM service that will allow you to connect to a CRM 4.0 instance. This code will be used by both the GetCRMData and PostCRMData Web methods, so it will reside in its own method. This method (shown in Figure 3) requires two key fields: the organization name of your CRM 4.0 instance and the URL of the main CRM service (located at /MSCRMServices/2007/crmservice.asmx). Note that these fields are best housed in a configuration file for easy modification after compiling the code.
Figure 3 The GetCRMService Method
static public CrmService GetCRMService()
{
CrmService service = new CrmService();
CrmAuthenticationToken token =
new Microsoft.Crm.Sdk.CrmAuthenticationToken();
token.OrganizationName = "Contoso";
service.Url = "https://localhost:5555/MSCRMServices/2007/crmservice.asmx";
service.Credentials = System.Net.CredentialCache.DefaultCredentials;
service.CrmAuthenticationTokenValue = token;
return service;
}
Querying CRM Data
You can now turn your attention to querying data from CRM 4.0. It’s important to know there are two types of entities in CRM 4.0: core system entities and custom entities. The core system entities can be worked with a little more easily than custom entities. By default, all of the properties on core entities can be retrieved through strongly typed objects in C#. Custom entities are generally queried as Dynamic Entities, although an alternative allows them to be treated as strongly typed objects, also. I’ll demonstrate querying data with both types of entities.
An example of querying a core system entity—the System User entity—is shown in Figure 4. This code replaces the Web method by the same name that was stubbed out earlier in this article. In this new code, which queries CRM 4.0 for all system users, you’ll see several important things. First, the type of object dealt with is “systemuser.” All of the core entities have their own types. Second, the result being returned is a string representation of an XML document.
Figure 4 Querying the System User Entity
[WebMethod]
public string GetCRMData()
{
// This will return all users in CRM in a single XML structure.
StringBuilder xml = new StringBuilder();
CrmService service = GetCRMService();
QueryExpression query = new QueryExpression();
query.EntityName = "systemuser";
query.ColumnSet = new AllColumns();
RetrieveMultipleRequest retrieve = new RetrieveMultipleRequest();
retrieve.Query = query;
retrieve.ReturnDynamicEntities = false;
RetrieveMultipleResponse retrieved =
(RetrieveMultipleResponse)service.Execute(retrieve);
xml.Append("<Users>");
for (int i = 0; i <
retrieved.BusinessEntityCollection.BusinessEntities.Count; i++)
{
systemuser user =
(systemuser)retrieved.BusinessEntityCollection.BusinessEntities[i];
// Create a string represenation to return to Silverlight app.
xml.Append("<User");
xml.Append(" FirstName ='" + user.firstname + "'");
xml.Append(" LastName = '" + user.lastname + "'");
xml.Append(" SystemUserId = '" + user.systemuserid.ToString() + "'");
xml.Append(" JobTitle = '" + user.jobtitle + "'");
xml.Append("/>");
}
xml.Append("</Users>");
return xml.ToString();
}
You’ll find that the options for returning data to Silverlight are pretty limited. For example, you can’t return the BusinessEntityCollection to work with, because Silverlight can’t work with the API directly. Second, there are limitations with passing XML to Silverlight via a Web service. So, in the end, dealing with a simple string is likely your best option.
Querying custom entities can be a little more involved. The most common way to retrieve data is by using a Dynamic Entity to retrieve the results (an example of this kind of retrieval is shown in Figure 5). The challenge to this approach is in dealing with specific attributes in filters within query expressions. While everything is possible, IntelliSense can’t help out much.
Figure 5 Retrieval Using a Dynamic Entity
public static DynamicEntity GetCRMEntity(
CrmService tmpService, String entityId, String entityName)
{
DynamicEntity crmEntity = null;
TargetRetrieveDynamic targetRetrieve = new TargetRetrieveDynamic();
// Set the properties of the target.
targetRetrieve.EntityName = entityName;
targetRetrieve.EntityId = new Guid(entityId);
// Create the request object.
RetrieveRequest retrieve = new RetrieveRequest();
// Set the properties of the request object.
retrieve.Target = targetRetrieve;
retrieve.ColumnSet = new AllColumns();
// Retrieve as a DynamicEntity.
retrieve.ReturnDynamicEntities = true;
// Execute the request.
RetrieveResponse retrieved = (RetrieveResponse)tmpService.Execute(retrieve);
// Extract the DynamicEntity from the request.
DynamicEntity entity = (DynamicEntity)retrieved.BusinessEntity;
crmEntity = entity;
return crmEntity;
}
Although the Dynamic Entity approach may be the most common, if you want to have full visibility into the structure of your entity in Visual Studio and interact with it in the same way as a standard entity, you can create a proxy to the CrmService. In many cases, this can greatly improve the development experience and enable more flexibility in how code can be written. A proxy is nothing more than a generated C# file, based on the most current instance of the CrmService WSDL. To create a proxy class for the main CrmService service, open a Visual Studio command prompt and type the following, replacing the URL with the appropriate link to your crmservice.asmx page:
wsdl.exe /out:CrmSdk.cs /namespace:CRM40SilverlightDemo.WebReferences.CrmSdkhttps://localhost:5555/mscrmservices/2007/crmservice.asmx?wsdl
This command will create a C# file called CrmSdk.cs in the directory from where you ran the wsdl.exe executable. This file should be added to your project. Once added, you can work with any custom entity in the exact same manner as core system entities. If the entity changes, simply update your proxy C# file and the new attributes (or other modifications) will be available. For the purposes of the current exercise, the proxy C# file won’t be used.
Updating CRM 4.0 Data
Having looked at how to retrieve data from CRM 4.0, you can now work through posting data. The code shown in Figure 6 shows how to update a system user record. It requires that two properties be passed in: the unique identifier of the CRM 4.0 record and the nickname. In order to pass in these strings, one line of code must be modified in the MainPage.xaml.cs, shown here:
proxyPost.PostCRMDataAsync("f04b02d9-ad5f-e011-a513-000c29330bd5","My Nickname");
Notice that the ID is hardcoded in the call to the PostCRMData method. You’ll want to come up with a mechanism to grab the ID dynamically.
Figure 6 Posting CRM 4.0 Data
[WebMethod]
public string PostCRMData(string userId, string nickname)
{
CrmService service = GetCRMService();
Key kid = new Key();
kid.Value = new Guid(userId);
systemuser entity = new systemuser();
entity.systemuserid = kid;
entity.nickname = nickname;
service.Update(entity);
return "success";
}
Processing the Results in Silverlight
At this point, the solution should be retrieving and posting data to and from CRM 4.0. However, nothing is being done with the string results that are returned to Silverlight. The GetCRMData method is returning a string of data that contains an XML document with all of the user records, but what can be done with that? Depending on the control, you may be able to bind to XML directly, or you may want to parse through the XML that’s returned and deal with individual data elements.
An example of looping through the results returned can be seen in Figure 7. This code shows how to load the string into an XML document and loop through the data. In working with XML documents in Silverlight, the most versatile functionality comes from the XDocument class. This can be accessed by adding a reference to System.Xml.Linq in your Silverlight project.
Figure 7 Working with XDocuments
void proxy_GetCRMDataCompleted(object sender, GetCRMDataCompletedEventArgs e)
{
XDocument xDoc = XDocument.Parse(e.Result);
string firstName;
string lastName;
string ID;
string title;
// Loop through the results.
foreach (XElement element in xDoc.Descendants("User"))
{
firstName = GetAttributeValue(element, "FirstName");
lastName = GetAttributeValue(element, "LastName");
ID = GetAttributeValue(element, "SystemUserId");
title = GetAttributeValue(element, "JobTitle");
}
}
private string GetAttributeValue(XElement element, string strAttributeName)
{
if (element.Attribute(strAttributeName) != null)
{
return element.Attribute(strAttributeName).Value;
}
return string.Empty;
}
Endless Possibilities
There are endless possibilities when integrating the two technologies. Some of the immediate next steps you might want to look at are approaches to exception handling (many Silverlight controls hide exceptions, so you’ll need to deal with this on a case-by-case basis) and integrating with various controls. Regardless of the direction you take, you’ve now got everything you need in order to build Silverlight solutions that read and write data with CRM 4.0.
Mark Beckner is the founder of Inotek Consulting Group LLC. He works across the Microsoft stack, including BizTalk, SharePoint, Dynamics CRM and general .NET Framework development. He can be reached at mmbeckner@inotekgroup.com.
Thanks to the following technical expert for reviewing this article: Scott Jones