BizTalk Server 2013 R2: Integration with MS Dynamics CRM 2015
Introduction
This article explains a step by step procedure to integrate BizTalk Server 2013 R2 with MS Dynamics CRM 2015 for creating and updating a contact entity.
External applications can be integrated with MS Dynamics CRM 2015 using the following two procedures:
- SOAP based – by consuming the organization service.
- An url leveraging the CRM SDK; with this SDK we can integrate with CRM using a strongly typed object model for each entity.
Each procedure has their own pros and cons and the decision to use either one purely depends on the context.
Based on my experience, Soap based is widely used from BizTalk server as we can retry the submissions in case of failures and the SDK method is just like using an inline send to CRM from BizTalk and we don’t have any kind of luxury to retry the failed submissions. It will be just like consuming a web service build in C#.Net.
In this article we will focus on Option#1 and discuss the step by step procedure to integrate BizTalk with CRM.
Create BizTalk project
Get the Organization service url from CRM
In order to consume the CRM organization service, we need to generate the schemas for the wsdl in BizTalk project.
We can retrieve the organization service URL from CRM with this navigation
- Login to CRM portal in browser
- Go to Settings – > Customizations -> Developer Resources
Generate Schemas
Create a new empty BizTalk server project in visual studio and name it as “BizTalkToDynamicsCRM2015”
- In BizTalk Solution right-click Project, select **Add **and then Add Generated Items; select Consume WCF Service Wizard and select Option1 Metadata Exchange (MEX) endpoint and in the next screen give the Organization service URL that we retrieved in Step1. Please give the url in the below format and then Click “Get”
https://xxxx/abc/XRMServices/2011/Organization.svc?wsdl
- Click Next and Import.
If we go to the BizTalk solution explorer we can see several generated schemas, a sample orchestration and a custom bindings file.
Unfortunately these schemas won’t work in practical. When we try opening any schema it displays error; below a screenshot of the error is shown when U tried opening one of the schema in the project.
Install CRM SDK 2015
The work around is to replace these schemas with the schemas shipped with CRM SDK.
Delete the 13 highlighted schemas from the solution explorer and replace these 7 new schemas from the SDK folder.
We can get the new schemas by downloading the CRM SDK through:
After downloading and installing the SDK, we can find the schemas in the below path.
- Installationdirectory/SDK/Schemas/CRMBizTalkIntegration.
- In BizTalk solution right-click project, select Add and then existing items; navigate to the SDK schemas folder and select the 7 schemas that have prefix “OrganizationService_”. After adding the new schemas the project looks like below.
So the main schema that we need to map the values is “OrganizationService_schemas_microsoft_com_xrm_2011_Contracts_Services.xsd”
The schema has different methods for different operations to perform in CRM and this takes the key/value pair instead of typed object. Key is the field name inside CRM entity and value is value of the field that we want to populate.
With this we have successfully added all the working CRM schemas to BizTalk project.
Solution Design for CRM operations
Input Message
BizTalk receives a customer xml message with key/value pair and need to create/update a contact in CRM. We are going to use a canonical schema pattern here by creating a typed customer object schema. Irrespective of type of message that BizTalk receives we want to have an enterprise level customer schema so that everyone will design the solutions based on that. Here is the screenshot of my input message which is key/value pair.
The input message schema looks like below.
And input xml message looks like below.
Our objective is to create/update a contact entity in CRM by receiving the above key/value pair XML.
Canonical Schema
Create a canonical customer schema. It is straight forward process in BizTalk to create a new schema for xml message and after creating the schema it looks like below.
The above schema represents an enterprise customer with all fields related to customer object.
Transform Key/Value message to Canonical
Create a transform to map the key/value pair to this customer canonical message. Here we need to use a combination of functoids logical equal to and value mapping functoid. If the key name is “FirstName” then value tag contains name of the person and if the key name is “Email” then Value element contains email address.
Below is the screenshot of the map.
Though the map looks complex it is pretty easy with a logical equal to and value mapping functoid to each element that we have to map on the customer canonical side.
If we want to map email address give the name of the key in logical equal to factoid and map the output of logical equal to value mapping flattening functoid.
Create separate grid pages for Phone number and addressed because it is very easy to map these using custom XSLT rather than functoids.
The phone numbers grid page looks like below.
Drag 3 scripting functoids and give the below xslt code for Home,Mobile and work phone types.
To map to Home Phone
<PhoneNumber>
<Type><xsl:value-of select="'Home'"/></Type>
<Number><xsl:value-of select="//*[local-name()='characteristic'][*[local-name()='name']='Application Data View.Applicant[1].Home Phone Number']/*[local-name()='value']"/></Number>
</PhoneNumber>
To map to Mobile Phone
<PhoneNumber>
<Type><xsl:value-of select="'Mobile'"/></Type>
<Number><xsl:value-of select="//*[local-name()='characteristic'][*[local-name()='name']='Application Data View.Applicant[1].Personal Mobile Number']/*[local-name()='value']"/></Number>
</PhoneNumber>
To map to Work Phone
<PhoneNumber>
<Type><xsl:value-of select="'Work'"/></Type>
<Number><xsl:value-of select="//*[local-name()='characteristic'][*[local-name()='name']='Application Data View.Applicant[1].Employer Phone Number']/*[local-name()='value']"/></Number>
</PhoneNumber>
Addresses are also mapped to Home address and postal address using custom XSLT.
To map to Home Address
<Address>
<Type>
<xsl:value-of select="'Home'"/>
</Type>
<AddressNumber1>
<xsl:value-of select="//*[local-name()='characteristic'][*[local-name()='name']='Application Data View.Applicant[1].Address[1].Address Number 1']/*[local-name()='value']"/>
</AddressNumber1>
<AddressNumber2>
<xsl:value-of select="//*[local-name()='characteristic'][*[local-name()='name']='Application Data View.Applicant[1].Address[1].Address Number 2']/*[local-name()='value']"/>
</AddressNumber2>
<AddressLine2>
<xsl:value-of select="concat(//*[local-name()='characteristic'][*[local-name()='name']='Application Data View.Applicant[1].Address[1].Address Line 1']/*[local-name()='value'],' ',//*[local-name()='characteristic'][*[local-name()='name']='Application Data View.Applicant[1].Address[1].Address Line 2']/*[local-name()='value'])"/>
</AddressLine2>
<AddressLine3>
<xsl:value-of select="//*[local-name()='characteristic'][*[local-name()='name']='Application Data View.Applicant[1].Address[1].Address Line 3']/*[local-name()='value']"/>
</AddressLine3>
<AddressLine4>
<xsl:value-of select="//*[local-name()='characteristic'][*[local-name()='name']='Application Data View.Applicant[1].Address[1].Address Line 4']/*[local-name()='value']"/>
</AddressLine4>
<AddressLine5>
<xsl:value-of select="//*[local-name()='characteristic'][*[local-name()='name']='Application Data View.Applicant[1].Address[1].Address Line 5']/*[local-name()='value']"/>
</AddressLine5>
<PostCode>
<xsl:value-of select="//*[local-name()='characteristic'][*[local-name()='name']='Application Data View.Applicant[1].Address[1].Address Postcode']/*[local-name()='value']"/>
</PostCode>
<Country>
<xsl:value-of select="//*[local-name()='characteristic'][*[local-name()='name']='Application Data View.Applicant[1].Address[1].Address Country']/*[local-name()='value']"/>
</Country>
</Address>
To map to Postal Address
<Address>
<Type>
<xsl:value-of select="'Postal'"/>
</Type>
<AddressNumber1>
<xsl:value-of select="//*[local-name()='characteristic'][*[local-name()='name']='Application Data View.Applicant[1].Address[5].Address Number 1']/*[local-name()='value']"/>
</AddressNumber1>
<AddressNumber2>
<xsl:value-of select="//*[local-name()='characteristic'][*[local-name()='name']='Application Data View.Applicant[1].Address[5].Address Number 2']/*[local-name()='value']"/>
</AddressNumber2>
<AddressLine2>
<xsl:value-of select="concat(//*[local-name()='characteristic'][*[local-name()='name']='Application Data View.Applicant[1].Address[5].Address Line 1']/*[local-name()='value'],' ' ,//*[local-name()='characteristic'][*[local-name()='name']='Application Data View.Applicant[1].Address[5].Address Line 2']/*[local-name()='value'])"/>
</AddressLine2>
<AddressLine3>
<xsl:value-of select="//*[local-name()='characteristic'][*[local-name()='name']='Application Data View.Applicant[1].Address[5].Address Line 3']/*[local-name()='value']"/>
</AddressLine3>
<AddressLine4>
<xsl:value-of select="//*[local-name()='characteristic'][*[local-name()='name']='Application Data View.Applicant[1].Address[5].Address Line 4']/*[local-name()='value']"/>
</AddressLine4>
<AddressLine5>
<xsl:value-of select="//*[local-name()='characteristic'][*[local-name()='name']='Application Data View.Applicant[1].Address[5].Address Line 5']/*[local-name()='value']"/>
</AddressLine5>
<PostCode>
<xsl:value-of select="//*[local-name()='characteristic'][*[local-name()='name']='Application Data View.Applicant[1].Address[5].Address Postcode']/*[local-name()='value']"/>
</PostCode>
<Country>
<xsl:value-of select="//*[local-name()='characteristic'][*[local-name()='name']='Application Data View.Applicant[1].Address[5].Address Country']/*[local-name()='value']"/>
</Country>
</Address>
We can validate the map by giving the input XMLmessage to the map and see whether it transforms to customer canonical and here is the screenshot of the output message.
After getting the customer canonical message we can map this message to CRM schema to create/update a contact. Create contact and update contact takes the same input and for the Update contact we specify the GUID of contact entity for which we are going to update and for create contact we don’t need to specify any value for GUID as it returns in the response as a result of contact creation.
Transform Canonical to CRM contact Update
Create a transform to map the customer canonical CRM contact update schema.
- Right click on project add and then add new items; Select Map and give the name of Map as “Trfm_CustomerCanonical_To_CrmContactUpdate.btm”
- Select the source schema as CustomerCanonical.xsd and target schema as “OrganizationService_schemas_microsoft_com_xrm_2011_Contracts_Services.xsd”
- Once we select the target schema a pop window displays for allowing us to select a specific CRM operation such as create/Update/execute etc. And in this case we select Update operation.
And the map looks like below.
Create four grid pages in the map just to make it easy for maintenance and also for understanding.
The first grid page “Entity name” just maps the Contact GUID for which we are updating the contact and Logical name as “contact” for identify the CRM Contact Entity.
- Map ID from canonical source to id field in the target schema and this ID consists of GUID for crm contact.
- Drag a string concatenate functoid on to the map and give the name as “contact” and map to LogicalName field in the CRM schema. This represents the entity name in CRM like contact, account etc..
Second grid page “Contact General” maps all the general information of the contact like name, email etc. Since the CRM contact update schema expects Key/value pairs again we have to use a custom XSLT for each of the field.
Since CRM expects a key/value pair we are going to map all the fields using custom XSLT. The XSLT code for any element remains same except the name of field. So here is the code for some of the fields and rest we can do in same way by just changing the crm field name.
Mapping Email
Drag and drop scripting functiod and give the source as email from input schema. Select “Inline XSLT call template” under scripting functoid configuration and give the below custom XSLT.
<xsl:template name="SetEmailValue">
<xsl:param name="param1" />
<KeyValuePairOfstringanyType xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<key xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic">emailaddress1</key>
<value xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:attribute name="xsi:type">
<xsl:value-of select="'xs:string'" />
</xsl:attribute>
<xsl:value-of select="$param1" />
</value>
</KeyValuePairOfstringanyType>
</xsl:template>
Mapping Date of Birth
For this we need to use an extra script funtoid to format date time value as CRM uses UTC date time values.
- Drag and drop scripting functiod and give the source as DOB from input schema. Select Inline C# and give the below function code.
public string ReturnUTCDate(string param1)
{
try
{
return System.DateTime.SpecifyKind(System.DateTime.Parse(param1), DateTimeKind.Utc).ToString("yyyy-MM-ddTHH:mm:ss");
}
catch
{
return param1;
}
}
Output of this functoid goes as input to Custom XSLT functoid.
<xsl:template name="SetDOBValue">
<xsl:param name="param1" />
<KeyValuePairOfstringanyType xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<key xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic">birthdate</key>
<value xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:attribute name="xsi:type">
<xsl:value-of select="'xs:dateTime'" />
</xsl:attribute>
<xsl:value-of select="$param1" />
</value>
</KeyValuePairOfstringanyType>
</xsl:template>
Mapping Gender
It is slightly different than other fields. The Gender field is defined as “OptionSet” in CRM which is like dropdown box and then it takes specific values within the range. So the custom XSLT should reflect with this OptionSet definition.
<xsl:template name="SetGenderValue">
<xsl:param name="param1" />
<KeyValuePairOfstringanyType xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<key xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic">gendercode</key>
<value xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts">
<xsl:attribute name="i:type">
<xsl:value-of select="'a:OptionSetValue'" />
</xsl:attribute>
<a:Value>
<xsl:value-of select="$param1" />
</a:Value>
</value>
</KeyValuePairOfstringanyType>
</xsl:template>
So we should keep giving the custom XSLT function for all fields and the only difference is field name. The code remains same for any field mapping.
Mapping First Name
<xsl:template name="SetFNameValue">
<xsl:param name="param1" />
<KeyValuePairOfstringanyType xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<key xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic">firstname</key>
<value xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:attribute name="xsi:type">
<xsl:value-of select="'xs:string'" />
</xsl:attribute>
<xsl:value-of select="$param1" />
</value>
</KeyValuePairOfstringanyType>
</xsl:template>
Mapping SurName
<xsl:template name="SetSurNameValue">
<xsl:param name="param1" />
<KeyValuePairOfstringanyType xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<key xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic">lastname</key>
<value xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:attribute name="xsi:type">
<xsl:value-of select="'xs:string'" />
</xsl:attribute>
<xsl:value-of select="$param1" />
</value>
</KeyValuePairOfstringanyType>
</xsl:template>
The third grid page contains the mapping for Phone numbers.
Mapping work Phone
<KeyValuePairOfstringanyType xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<key xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic">telephone1</key>
<value xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:attribute name="xsi:type">
<xsl:value-of select="'xs:string'" />
</xsl:attribute>
<xsl:value-of select="//*[local-name()='PhoneNumber'][*[local-name()='Type']='Work']/*[local-name()='Number']"/>
</value>
</KeyValuePairOfstringanyType>
Mapping Home Phone
<KeyValuePairOfstringanyType xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<key xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic">telephone3</key>
<value xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:attribute name="xsi:type">
<xsl:value-of select="'xs:string'" />
</xsl:attribute>
<xsl:value-of select="//*[local-name()='PhoneNumber'][*[local-name()='Type']='Home']/*[local-name()='Number']"/>
</value>
</KeyValuePairOfstringanyType>
Mapping Mobile Phone
<KeyValuePairOfstringanyType xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<key xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic">mobilephone</key>
<value xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:attribute name="xsi:type">
<xsl:value-of select="'xs:string'" />
</xsl:attribute>
<xsl:value-of select="//*[local-name()='PhoneNumber'][*[local-name()='Type']='Mobile']/*[local-name()='Number']"/>
</value>
</KeyValuePairOfstringanyType>
The fourth grid page contains the mapping for Addresses.
The customer canonical contains the address types as “Home” and “Postal” but CRM expects different ids for these so I maintained these mapping inside a custom table in SQL and using a DBLookup functoid I retrieve the crm addressTypeid for a canonical address type and the table looks like below. The CRM address type ids can change any time based on environment and also depends on the CRM developer so maintaining these ids inside a table will stop us to re-deploy the BizTalk every time it changes.
So for a home address I map the value 3 and for a postal address I map 1 to CRM request. Since I need the Custom XSLT to map to CRM field I executed the DBLookup functoid inside the XSLT itself.
<xsl:template name="SetAddressValues">
<xsl:param name="paramHometype" />
<xsl:param name="paramPostaltype" />
<xsl:for-each select="//Address">
<xsl:choose>
<xsl:when test="Type='Home'">
<xsl:variable name="varstreettype" select="AddressLine3"></xsl:variable>
<xsl:variable name="varcon" select="ScriptNS0:ReadString("BizTalkToDynamicsCRM2015" , "DBLookupCon.BTMap")" />
<xsl:variable name="vardbcall" select="ScriptNS1:GetData(string($varcon) , "True" , "dbo.StreetTypeMapping" , "PCOCode" , string($varstreettype))" />
<xsl:variable name="varsttypevalue" select="ScriptNS2:GetValue(string($vardbcall) , "CRMCode")" />
<KeyValuePairOfstringanyType xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<key xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic">address1_addresstypecode</key>
<value xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts">
<xsl:attribute name="i:type">
<xsl:value-of select="'a:OptionSetValue'" />
</xsl:attribute>
<a:Value>
<xsl:value-of select="$paramHometype" />
</a:Value>
</value>
</KeyValuePairOfstringanyType>
<KeyValuePairOfstringanyType xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<key xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic">address1_line1</key>
<value xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:attribute name="xsi:type">
<xsl:value-of select="'xs:string'" />
</xsl:attribute>
<xsl:value-of select="AddressLine1"/>
</value>
</KeyValuePairOfstringanyType>
<KeyValuePairOfstringanyType xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<key xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic">address1_line2</key>
<value xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:attribute name="xsi:type">
<xsl:value-of select="'xs:string'" />
</xsl:attribute>
<xsl:value-of select="concat(AddressLine2,' ',string($varsttypevalue))"/>
</value>
</KeyValuePairOfstringanyType>
<KeyValuePairOfstringanyType xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<key xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic">address1_line3</key>
<value xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:attribute name="xsi:type">
<xsl:value-of select="'xs:string'" />
</xsl:attribute>
<xsl:value-of select="AddressLine4"/>
</value>
</KeyValuePairOfstringanyType>
<KeyValuePairOfstringanyType xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<key xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic">address1_city</key>
<value xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:attribute name="xsi:type">
<xsl:value-of select="'xs:string'" />
</xsl:attribute>
<xsl:value-of select="AddressLine5"/>
</value>
</KeyValuePairOfstringanyType>
<KeyValuePairOfstringanyType xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<key xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic">address1_postalcode</key>
<value xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:attribute name="xsi:type">
<xsl:value-of select="'xs:string'" />
</xsl:attribute>
<xsl:value-of select="PostCode"/>
</value>
</KeyValuePairOfstringanyType>
<KeyValuePairOfstringanyType xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<key xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic">address1_country</key>
<value xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:attribute name="xsi:type">
<xsl:value-of select="'xs:string'" />
</xsl:attribute>
<xsl:value-of select="Country"/>
</value>
</KeyValuePairOfstringanyType>
</xsl:when>
<xsl:when test="Type='Postal'">
<xsl:variable name="varstreettype" select="AddressLine3"></xsl:variable>
<xsl:variable name="varcon" select="ScriptNS0:ReadString("BizTalkToDynamicsCRM2015" , "DBLookupCon.BTMap")" />
<xsl:variable name="vardbcall" select="ScriptNS1:GetData(string($varcon) , "True" , "dbo.StreetTypeMapping" , "PCOCode" , string($varstreettype))" />
<xsl:variable name="varsttypevalue" select="ScriptNS2:GetValue(string($vardbcall) , "CRMCode")" />
<KeyValuePairOfstringanyType xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<key xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic">address2_addresstypecode</key>
<value xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts">
<xsl:attribute name="i:type">
<xsl:value-of select="'a:OptionSetValue'" />
</xsl:attribute>
<a:Value>
<xsl:value-of select="$paramPostaltype" />
</a:Value>
</value>
</KeyValuePairOfstringanyType>
<KeyValuePairOfstringanyType xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<key xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic">address2_line1</key>
<value xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:attribute name="xsi:type">
<xsl:value-of select="'xs:string'" />
</xsl:attribute>
<xsl:value-of select="AddressLine1"/>
</value>
</KeyValuePairOfstringanyType>
<KeyValuePairOfstringanyType xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<key xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic">address2_line2</key>
<value xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:attribute name="xsi:type">
<xsl:value-of select="'xs:string'" />
</xsl:attribute>
<xsl:value-of select="concat(AddressLine2,' ',string($varsttypevalue))"/>
</value>
</KeyValuePairOfstringanyType>
<KeyValuePairOfstringanyType xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<key xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic">address2_line3</key>
<value xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:attribute name="xsi:type">
<xsl:value-of select="'xs:string'" />
</xsl:attribute>
<xsl:value-of select="AddressLine4"/>
</value>
</KeyValuePairOfstringanyType>
<KeyValuePairOfstringanyType xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<key xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic">address2_city</key>
<value xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:attribute name="xsi:type">
<xsl:value-of select="'xs:string'" />
</xsl:attribute>
<xsl:value-of select="AddressLine5"/>
</value>
</KeyValuePairOfstringanyType>
<KeyValuePairOfstringanyType xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<key xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic">address2_postalcode</key>
<value xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:attribute name="xsi:type">
<xsl:value-of select="'xs:string'" />
</xsl:attribute>
<xsl:value-of select="PostCode"/>
</value>
</KeyValuePairOfstringanyType>
<KeyValuePairOfstringanyType xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<key xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic">address2_country</key>
<value xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:attribute name="xsi:type">
<xsl:value-of select="'xs:string'" />
</xsl:attribute>
<xsl:value-of select="Country"/>
</value>
</KeyValuePairOfstringanyType>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:template>
Just validates this map by giving the customer canonical message. After validating the map the CRM update contact request message looks like below which is a valid CRM request.
Design Orchestration
Create the Message types
Create the Orchestration to send the request to CRM and get the response back. CRM 2015 uses Soap1.2 protocol so we also handle the soap faults inside the orchestration. If we don’t handle the soap fault then we see a generic error message “processing error” instead of getting actual error message back. With soap fault handling we can retrieve the correct error message from CRM organization service.
We have already got an empty orchestration “OrganizationService.odx” created when we consumed the organization service wsdl in BizTalk solution so let’s open this orchestration to define a work flow.
We use this orchestration either to create a contact in CRM or Update a contact in CRM based on the operation type that we get in the input key/value pair message. The input message contains a field value “Create” or “Update” and we decide based on this value.
Create 7 messages in orchestration properties.
- ContactCreate_Request of type “BizTalk_ToDynamicsCRM2015.OrganizationService_schemas_microsoft_com_xrm_2011_Contracts_Services.Create”
- ContactCreate_Response of type “BizTalk_ToDynamicsCRM2015.OrganizationService_schemas_microsoft_com_xrm_2011_Contracts_Services.CreateResponse”
- CRMContactUpdate_Request of type “BizTalk_ToDynamicsCRM2015.OrganizationService_schemas_microsoft_com_xrm_2011_Contracts_Services.Update”
- CRMContactUpdate_Response of type “BizTalk_ToDynamicsCRM2015.OrganizationService_schemas_microsoft_com_xrm_2011_Contracts_Services.UpdateResponse”
- FaultMessage_1_2 of type “BTS.soap_envelope_1__2.Fault” this can be found under BizTalk global property schemas.
- Customer_Canonical of type “BizTalk_ToDynamicsCRM2015.CustomerCanonical”
- PCOInput of type “BizTalk_ToDynamicsCRM2015.PCO”
Create two string variables.
- stringFaultMessage to populate the fault message that we get form soap fault
- varMessageType to access the type of operation from input message.
Create contact Orchestration
Steps Create the Orchestration to send the request to CRM
- Create a receive shape to receive the input file and rename the shape to “Receive_Input”.
- Select the receive shape and select the message “PCOInput”
- Create the receive port and use specify later.
- Drag an expression shape to access the message type from Input message to decide create contact or update contact.
- Drag a transform shape and select the existing map as “Trfm_PCO2CustomerCanonical.btm” to transform the key/value pair message to customer canonical
- Drag a decide shape to find whether it is create contact or update contact.
- Double click the first rule branch and give the expression varMessageType == "Create"
- Double click the second rule branch and give the expression varMessageType == "Update"
- Under Create rule branch, Create a scope and rename to “Scope_CRMContactCreate”
- Drag a transform shape and select the existing map as “Trfm_CustomerCanonical_To_CrmContactCreate.btm” to transform the customer canonical to CRM contact create request.
- Create a send and receive shape. Select the message “ContactCreate_Request” to send shape and “Receive_ContactCreate” for Receive shape.
- Click on the send port and create a two-way send port with specify later.
- Rename the “Operation_1” in the port to “Create” which is identify the soap action for Create contact.
- Right click on “Create” and select New Fault Message and rename the fault to “OrganizationServiceFault”
- Click on “OrganizationServiceFault” and in the properties windows select fault 1.2 schema “BTS.soap_envelope_1__2.Fault”
- Add an exception handler to the scope and rename it as Catch soap faults
- Give Exception Object name as “SoapFaultCreate” and Exception object type as “Port_crmsoapwebservice.Create.OrganizationServiceFault” This we get automatically when we added the fault handler on the CRM send port.
- Use a message assignment shape along with construct shape.
- Select the message type “FaultMessage_1_2” for construct shape.
- Give the below code in message assignment shape. This will assign the soapfault to fault message so that we can retrieve the error.
FaultMessage_1_2 = SoapFaultCreate;
- Drag an expression shape and rename it as “Log Error”
- We use the below xpath expression to retrieve the error message from the fault message.
- stringFaultMessage = xpath(FaultMessage_1_2, "string(/*[local-name()='Fault' and namespace-uri()='http://www.w3.org/2003/05/soap-envelope']/*[local-name()='Reason' and namespace-uri()='http://www.w3.org/2003/05/soap-envelope']/*[local-name()='Text' and namespace-uri()='http://www.w3.org/2003/05/soap-envelope'])");
Update contact Orchestration Steps
- Under Update rule branch, Create a scope and rename to “Scope_CRMContactUpdate”
- Drag a transform shape and select the existing map as “Trfm_CustomerCanonical_To_CrmContactUpdate.btm” to transform the customer canonical to CRM contact update request.
- Create a send and receive shape. Select the message “CRMContactUpdate_Request” to Send shape and “CRMContactUpdate_Response” for Receive shape.
- Right click on existing port “Port_crmsoapwebservice” and select new operation. Rename Operation_1 as Create
- Right click on “Update” and select New Fault Message and rename the fault to “OrganizationServiceFault”
- Click on “OrganizationServiceFault” and in the properties windows select fault 1.2 schema “BTS.soap_envelope_1__2.Fault”
- Add an exception handler to the scope and rename it as Catch soap faults
- Give Exception Object name as “SoapFault” and Exception object type as “Port_crmsoapwebservice.Update.OrganizationServiceFault” This we get automatically when we added the fault handler on the CRM send port.
- Use a message assignment shape along with construct shape.
- Select the message type “FaultMessage_1_2” for construct shape.
- Give the below code in message assignment shape. This will assign the soapfault to fault message so that we can retrieve the error.
FaultMessage_1_2 = SoapFault;
- Drag an expression shape and rename it as “Log Error”
- We use the below xpath expression to retrieve the error message from the fault message.
- stringFaultMessage = xpath(FaultMessage_1_2, "string(/*[local-name()='Fault' and namespace-uri()='http://www.w3.org/2003/05/soap-envelope']/*[local-name()='Reason' and namespace-uri()='http://www.w3.org/2003/05/soap-envelope']/*[local-name()='Text' and namespace-uri()='http://www.w3.org/2003/05/soap-envelope'])");
Finally the orchestration looks like below.
Deployment
Deploy Project
Right click on the project and select deploy
Once the application is deployed lets open the application in BizTalk administration console and create a send port for CRM.
In this case our CRM is hosted on https and configured with windows authentication to authenticate the clients. So we use the send port to configure the windows authentication
First we need to add the BizTalk host account in CRM portal users as System administrator role.
Create Send port for CRM
Create one receive location with FILE adapter and bind to the orchestration so that it picks the input message for processing
Create a two way send port with name “WcfSendPort_CRMCustom” and select the Binding type as WCF-Custom
Click on CONFIGURE and give the address (URI) as organization service URL
Give the below url mapping under Soap Action Header.
<BtsActionMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Operation Name="Create" Action="http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Create" />
<Operation Name="Retrieve" Action="http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Retrieve" />
<Operation Name="Update" Action="http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Update" />
<Operation Name="Delete" Action="http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Delete" />
<Operation Name="Execute" Action="http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute" />
<Operation Name="Associate" Action="http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Associate" />
<Operation Name="Disassociate" Action="http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Disassociate" />
<Operation Name="RetrieveMultiple" Action="http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/RetrieveMultiple" />
</BtsActionMapping>
Click on Binding tab and select the binding as “customBinding”
Give the properties as mentioned below in the screenshots to enable windows authentication.
And click on Messages Tab check propagate fault message.
Testing
Drop the input key/value pair message in to a file receive location so that Orchestration picks the file for processing.
The input file looks like below
The orchestration immediately picks this file and create/update contact in CRM.
Open CRM and search for the contact Potter and the contact is created.
And for testing the update contact, drop the same input file but with a GUID field inside the input file. GUID is used to update the CRM contact entity.
I updated the Relationship type to Couple and it is reflected in CRM contact page.
Conclusion
Hope this article helps to integrate BizTalk Server 2013 R2 with Dynamics CRM 2015. The code and Custom XSLT mentioned in this article remains same for creating any entity in CRM not only contact, only change is we need to specify the corresponding CRM entity name and field names in the XSLT.
Important TIP:
Normally it is tough to know the soap request format for each CRM operation with data types and field names in order to create the same xml request from BizTalk. So I found an easy way to get the soap request from SDK. CRM SDK is shipped with a sample c# console application “SoapLogger” to consume the organization service. So open the project in visual studio and populate the corresponding entity and field names (it has typed objects so when we type Contact class then we automatically get all the fields like FirstName, LastName etc…in visual studio) and run the project. This application writes the soap request to a test file and the file path is configured in app.config file. Take this soap request and try to create the same from BizTalk map is easy.
See Also
Another important place to find a huge amount of BizTalk related articles is the TechNet Wiki itself. The best entry point is BizTalk Server Resources on the TechNet Wiki.