Monday 30 September 2013

Integrating Microsoft Dynamics CRM 2011 with BizTalk 2010


As I told in my previous post, I am working at a customer where we are trying to integrate Microsoft Dynamics CRM 2011 using BizTalk 2010. Since CRM 2011 does not have a BizTalk adapter anymore, we have to use the webservices it exposes, which can be used in BizTalk in 2 ways. I first tried to use the untyped option, where you would have to use mappings with custom XSLT for your communication to the CRM service. I ran into a lot of problems with this, and then decided to use the typed option instead. Using this option, you will create a helper class that does the communication to CRM, which you can then use from your orchestrations. While looking for more resources on this, I ran into this video Peter Kelcey created, which I used as my basis for my solution. The full source for my solution can be found here.

Get CRM Types

The first thing you will need to do is to get the CRM 2011 SDK if you have not done so yet. In the SDK you will find a file called crmsvcutil.exe. Using this, you create a helper class containing all your CRM entities. To use this, execute it from the command line as following:
"C:\Location\Of\Microsoft Dynamics CRM 2011 SDK\bin\crmsvcutil.exe" /url:https://company.crm.ms-hosting.nl/XRMServices/2011/Organization.svc /out:CRMHelper.cs /username:user@company.com /password:Pass@word1
The first parameter, url, is the URL where your organization service is living. The second parameter, out, is the class that will be created. The username and password parameters are the credentials of a user in CRM with enough rights to actually use the webservice. Once you have created the helper class you can add this to your BizTalk project.

Service Wrapper

Next we are going to create a new class, that will hold various helper classes used communicate with CRM. I simply call this class HelperMethods.cs. Make sure you add a reference to microsoft.xrm.sdk.dll, which can be found in the CRM 2011 SDK.
using System;
using System.Collections.Generic;
using System.IO;
using System.ServiceModel.Description;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Query;
 
namespace Company.BizTalk.CRM.BusinessComponents
{
    /// <summary>
    /// Methods used to communicate with Microsoft Dynamics CRM 2011.
    /// </summary>
    internal static class HelperMethods
    {
 
	}
}
The first method we are going to set up is where we create a connection to CRM. As always, I have extensively commented my code, so it should be pretty easy to see what everything does. I have a seperate Constants class, where I have stored several strings for username, password, etc. The method finally returns a connection to our CRM system.
/// <summary>
/// This method opens a connection to CRM 2011 and returns a proxy object that can be used to create, update, read, delete and execute events in CRM.
/// </summary>
/// <returns>The proxy used to connect to the CRM webservice.</returns>
internal static OrganizationServiceProxy CreateServiceProxy()
{
	// The proxy used to connect to the CRM webservice	OrganizationServiceProxy serviceProxy;
	// Set the URL for the CRM organization webservice
	Uri organizationUri = new Uri("https://" + Constants.CRMOrganizationName + ".crm.ms-hosting.nl/XRMServices/2011/Organization.svc");
 
	// The user credentials we need to provide are the credentials that we use to access CRM
	ClientCredentials credentials = new ClientCredentials(); 	
	credentials.UserName.UserName = Constants.CRMUserName;	
	credentials.UserName.Password = Constants.CRMPassword;
 
	// This is only used when you want to connect to CRM online
	// Seeing how I use the on-promise CRM I will not be using this, but I will let it in for reference purposes
	// The device credentials that we use must have been generated by the CreateCRM2011Device application, which is available at http://code.msdn.microsoft.com/CRM2011Beta/Release/ProjectReleases.aspx?ReleaseId=4944
	// If you do not have these credentials, download the application, run it and a new Device username and Device password will be generated for you
	ClientCredentials devicecredentials = new ClientCredentials();
 
	// Create the service proxy
	serviceProxy = new OrganizationServiceProxy(organizationUri, null, credentials, devicecredentials);
 
	// This next method enables support for early bound types
	// If you forget this line, you'll get a "value cannot be null" exception
	serviceProxy.EnableProxyTypes();
 
	// Return the proxy
	return serviceProxy;
}
And here is what the constants class looks like.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace Company.BizTalk.CRM.BusinessComponents
{
    /// <summary>
    /// Class containing all constant values used in the helper classes.
    /// </summary>
    internal static class Constants
    {
        /// <summary>
        /// The name of the column with the email addresses for contacts.
        /// </summary>
        internal static string ContactEmailAddressColumnName { get { return "emailaddress1"; } }
 
        /// <summary>
        /// The name of the organization used to connect to the CRM webservice.
        /// </summary>
        internal static string CRMOrganizationName { get { return "company"; } }
 
        /// <summary>
        /// The username used to connect to the CRM webservice.
        /// </summary>
        internal static string CRMUserName { get { return "eldert.grootenboer@company.nl"; } }
 
        /// <summary>
        /// The password used to connect to the CRM webservice.
        /// </summary>
        internal static string CRMPassword { get { return "Pass@word1"; } }
    }
}

Converting schema’s to C# classes

To be able to easily use the schema’s from BizTalk in our code, we should create C# classes to do this. For this the xsd.exe tool can be used. This tool can be found in the folder
Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools
Using this tool you can create a C# class with schema by running the following from a commandline:
Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools\xsd.exe C:\Location\Of\YourSchema.xsd /c /o:C:\Location\Of
If you use the same output folder as the where the input xsd file is located, you can attach it to the schema in Visual Studio by using the Show All Files button, and then including it in the project. Do this for all schema’s you will be receiving from your orchestration (probably your canonicals), which will contain the objects you want to transfer to or from CRM.

C# to XML Documents

Since we only want to use XMLDocuments in BizTalk itself, we are also going to create a class which is used to create XMLDocuments from our classes representing the schema’s (which we created in the previous chapter). To do this, we will add another method to the class we just created. This method will take a C# object, and serialize it to a XML document.
/// <summary>
/// Create an XML document from a C# object.
/// </summary>
/// <param name="objectToMakeXmlDocument">The object to be serialized.</param>
/// <returns>The serialized object as an XML document.</returns>
internal static XmlDocument CreateXmlDocument(object objectToMakeXmlDocument)
{
	// The serializer we will use
	var xmlSerializer = new XmlSerializer(objectToMakeXmlDocument.GetType());
 
	// Create a new StringBuilder, that will create a string with the XML document
	var stringBuilder = new StringBuilder();
 
	// Serialize the object into the StringBuilder
	xmlSerializer.Serialize(new StringWriter(stringBuilder), objectToMakeXmlDocument);
 
	// Create an XML document
	XmlDocument document = new XmlDocument();
 
	// Load the XML from the StringBuilder
	document.LoadXml(stringBuilder.ToString());
 
	// Return the XML document
	return document;
}

CRM Actions

The next class we are going to create will contain the actions we want do on CRM, like retrieve, insert, update and delete.
using System;
using System.Collections.Generic;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
 
namespace Company.BizTalk.CRM.BusinessComponents
{
    /// <summary>
    /// Class containing the various actions that can be done in CRM, like select, create, update, etc.
    /// </summary>
    internal static class CRMActions
    {
 
	}
}
My first method in this class will be used to look up entities. CRM has to retrieve functions, one uses the ID of an entity, the other can use a custom condition. Since we wanted to be able to find contacts by using the email address, I used the second option. This method creates the query to be used, sends this to CRM, and returns the entities that were returned by CRM.
/// <summary>
/// Retrieves multiple entities using a query.
/// </summary>
/// <param name="entityName">The logical name of the entity which we want to work on.</param>
/// <param name="conditions">The query to be used.</param>
/// <returns>A collection of entities.</returns>
internal static DataCollection<Entity> RetrieveMultipleEntities(string entityName, List<ConditionExpression> conditions)
{
	// Create the proxy
	var serviceProxy = HelperMethods.CreateServiceProxy();
 
	// Execute the query and return the result
	return serviceProxy.RetrieveMultiple(HelperMethods.CreateRetrieveMultipleQuery(entityName, conditions)).Entities;
}
As you can see a list of ConditionExpression is used, which is used to set up a filter. It contains a column on which we want to filter, the operator to be used (f.e. equals or like), and the value we want to find.
Also I use a method called CreateRetrieveMultipleQuery in the HelperMethods class. This method is used to create the QueryExpression that will be sent to the CRM webservice, and looks like this:
/// <summary>
/// Create a query that will retrieve multiple entities from CRM.
/// </summary>
/// <param name="entityName">The logical name of the entity.</param>
/// <param name="conditionExpressions">The conditions that make up the query.</param>
/// <returns>A query that can be used to retrieve multiple entities.</returns>
internal static QueryExpression CreateRetrieveMultipleQuery(string entityName, IEnumerable<ConditionExpression> conditionExpressions)
{
	// Create a new FilterExpression
	var filterExpression = new FilterExpression();
 
	// Add the conditions to set up the filter
	filterExpression.Conditions.AddRange(conditionExpressions);
 
	// Create the new Query expression and return it
	return new QueryExpression
	{
		EntityName = entityName,
		ColumnSet = new ColumnSet
		{
			AllColumns = true
		},
		Criteria = filterExpression
	};
}
This next method is used to create a new entity, and returns it’s ID.
/// <summary>
/// Create a new entity in CRM.
/// </summary>
/// <param name="entityToCreate">The entity that should be created.</param>
/// <returns>The GUID of the new entity.</returns>
internal static Guid CreateEntity(Entity entityToCreate)
{
	// Create the proxy
	var serviceProxy = HelperMethods.CreateServiceProxy();
 
	// Create the entity, and return it's GUID
	return serviceProxy.Create(entityToCreate);
}
We also want a method we can use to update an entity.
/// <summary>
/// Update an existing entity in CRM.
/// </summary>
/// <param name="entityToUpdate">The entity that should be updated, with it's new values set.</param>
internal static void UpdateEntity(Entity entityToUpdate)
{
	// Create the proxy
	var serviceProxy = HelperMethods.CreateServiceProxy();
 
	// Update the entity
	serviceProxy.Update(entityToUpdate);
}
The last method in this class will be used to delete an entity in CRM.
/// <summary>
/// Delete an entity in CRM.
/// </summary>
/// <param name="entityToDelete"></param>
internal static void DeleteEntity(Entity entityToDelete)
{
	// Create the proxy
	var serviceProxy = HelperMethods.CreateServiceProxy();
 
	// Delete the entity
	serviceProxy.Delete(entityToDelete.LogicalName, entityToDelete.Id);
}

Contact proxy

For this blogpost I will only show the code I use for working on one entity, contact. All code up to this point can be re-used for all entities, so you only need to to create the class I am going to describe now for each entity you want to work on from BizTalk. Seeing how this is the class you will actually be using in your orchestration, you have to make it serializable, so BizTalk can write it to the database.
using System;
using System.Linq;
using System.Xml;
using Microsoft.XLANGs.BaseTypes;
using Microsoft.Xrm.Sdk.Query;
using System.Collections.Generic;
 
namespace Customer.BizTalk.CRM.BusinessComponents
{
    /// <summary>
    /// Proxy class used for working on contacts in CRM.
    /// </summary>
    [Serializable]
    public class CRMServiceProxyContact
    {
 
	}
}
This is where you will use the C# classes you created of your schemas. In this class, add an attribute for the type of schema you will be getting from BizTalk.
/// <summary>
/// Canonical representation of the contact.
/// </summary>
ContactCanonical _contact;
The schema looks as following. Here IsProcessed is a boolean indicating if the message has allready been processed by a orchestration, and is promoted so it can be used on the filters. Action is an enumerator where a value of retrieve, create, update or delete can be used.

Next we are going to create the constructor. This takes in a XLANGMessage, which is the message from the orchestration in BizTalk. To be able to use this type add a reference to Microsoft.XLANGs.BaseTypes.dll which is located in the BizTalk 2010 installation folder.
/// <summary>
/// Constructor.
/// </summary>
/// <param name="serviceRequest">The XML document from BizTalk with the canonical contact.</param>
public CRMServiceProxyContact(XLANGMessage serviceRequest)
{
	// Cast the XML document to a canonical contact
	_contact = (ContactCanonical)serviceRequest[0].RetrieveAs(typeof(ContactCanonical));
 
	// We have processed the contact, so make sure it does not get picked up by this orchestration again
	_contact.Header.IsProcessed = true;
}
The next method is used to determine the action that should be done in CRM by looking into the message we got. We return the message with it’s new contents to the orchestration.
/// <summary>
/// Work on the contact.
/// </summary>
/// <returns>XML document with the result.</returns>
public XmlDocument DoWorkOnContact()
{
	// Check what type of work we want to do on the contact.
	switch (_contact.Header.Action)
	{
		case ContactCanonicalHeaderAction.retrieve:
			return RetrieveContact();
		case ContactCanonicalHeaderAction.create:
			return CreateContact();
		case ContactCanonicalHeaderAction.update:
			return UpdateContact();
		case ContactCanonicalHeaderAction.delete:
			return DeleteContact();
		default:
			throw new Exception("Invalid action.");
	}
}
Here is the code to retrieve a contact. We look up the contact by using it’s email address, then we enrich the message with the data we got from CRM.
/// <summary>
/// Retrieve a contact from CRM.
/// </summary>
/// <returns>The contact that was retrieved.</returns>
private XmlDocument RetrieveContact()
{
	// Get the contact we want to retrieve
	var retrievedContact = GetContactByEmailAddress();
 
	// Set the values from the retrieved contact on the contact that will be returned as an XML document
	// Basicly, we do a mapping from the result of the query to the BizTalk document
	_contact.Body.ID = retrievedContact.Id.ToString();
	_contact.Body.Name = retrievedContact.LastName;
 
	// Return the contact as an XML document
	return HelperMethods.CreateXmlDocument(_contact);
}
As you can see, we use a method called GetContactByEmailAddress, which is used to retrieve all contacts with a certain email address. Since we assume the email address is unique, this should return one contact.
/// <summary>
/// Get a contact by looking it up by email address.
/// </summary>
/// <returns>The contact belonging with the email address specified in the BizTalk message.</returns>
private Contact GetContactByEmailAddress()
{
	// Get the contact by looking it up by email address
	// The email address should be treated as a primary key, and therefor should never change
	var contacts = CRMActions.RetrieveMultipleEntities(Contact.EntityLogicalName, GetEmailAddressFilter());
 
	// Check if any contacts were found
	if (contacts.Any())
	{
		// Seeing how the email address should be unique, we can simply get the first contact
		return contacts.First() as Contact;
	}
 
	// The contact could not be found
	throw new Exception("No contact exists with this email address.");
}
We use another method here, GetEmailAddressFilter. This method simply creates a list of ConditionExpressions which will be used to set up the query.
/// <summary>
/// Create a filter that uses the email address.
/// </summary>
/// <returns></returns>
private List<ConditionExpression> GetEmailAddressFilter()
{
	return new List<ConditionExpression>
	{
                new ConditionExpression
                {
		        AttributeName = Constants.ContactEmailAddressColumnName,
		        Operator = ConditionOperator.Equal,
		        Values = { _contact.Body.EmailAddress }
                }
	};
}
The next method will be used to create a new contact. We insert the new contact into CRM, and return the message with the new ID set we got from CRM.
/// <summary>
/// Create a contact in CRM.
/// </summary>
/// <returns>The contact we just created, including the new ID.</returns>
private XmlDocument CreateContact()
{
	// Create a new contact
	Contact contactToUpdate = new Contact();
 
	// Set the values of the contact
	// Basicly, we do a mapping from the BizTalk document to a CRM contact
	contactToUpdate.LastName = _contact.Body.Name;
	contactToUpdate.EMailAddress1 = _contact.Body.EmailAddress;
 
	// Create the contact
	_contact.Body.ID = CRMActions.CreateEntity(contactToUpdate).ToString();
 
	// Return the contact as an XML document
	return HelperMethods.CreateXmlDocument(_contact);
}
Now let’s create a method used to update an existing contact. We first retrieve the contact by looking it up in CRM using it’s email address. We then set the new data, and then update the contact in CRM.
/// <summary>
/// Update a contact in CRM.
/// </summary>
/// <returns>The contact we updated with it's new values.</returns>
private XmlDocument UpdateContact()
{
	// Get the contact we want to update 
	var contactToUpdate = GetContactByEmailAddress();
 
	// Set the new values on the contact
	contactToUpdate.LastName = _contact.Body.Name;
 
	// Update the contact in CRM
	CRMActions.UpdateEntity(contactToUpdate);
 
	// Return the updated contact
	return HelperMethods.CreateXmlDocument(RetrieveContact());
}
And finally, the method to delete a contact. This again looks up the contact in CRM by using it’s email address, and then deletes the contact. Since we use the IsProcessed variable in the message to determine if a message has been processed, we create a new canonical contact to return to the orchestration.
/// <summary>
/// Delete a contact in CRM.
/// </summary>
/// <returns></returns>
private XmlDocument DeleteContact()
{
	// Get the contact we want to delete
	var contactToDelete = GetContactByEmailAddress();
 
	// Delete the entity from CRM
	CRMActions.DeleteEntity(contactToDelete);
 
	// Return a new contact to BizTalk, which just specifies that processing succeeded
	var doc = new ContactCanonical();
	doc.Header.IsProcessed = true;
	return HelperMethods.CreateXmlDocument(doc);
}
That was all the code we need. Now to be able to use all this, we need to create an orchestration.

On my receive I use the following filter:
(BTS.MessageType == "http://www.company.com/BizTalk/Schemas/v100#ContactCanonical") 
&& (Company.BizTalk.Algemeen.Schemas.IsProcessed == false)
And in my expression I use this:
crmServiceProxyContact = new Company.BizTalk.CRM.BusinessComponents.CRMServiceProxyContact(contactIncoming);
contactResponse = crmServiceProxyContact.DoWorkOnContact();
That is all, you can use this framework do do your communication with CRM. As you can see it is some more work then we are used to by using adapters, but it does work quite nicely. I have uploaded the entire solution here. Hopefully it can save you some time when integrating CRM 2011.

1 comment:

  1. i am for the first time here. I found this board and I in finding It truly helpful & it helped me out a lot. I hope to present something back and help others such as you helped me.
    PIPEDRIVE

    ReplyDelete