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.
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.
ReplyDeletePIPEDRIVE