다음을 통해 공유


Querying and Creating Records in Dynamics CRM 2011 from BizTalk Server 2010

Introduction

Microsoft Dynamics CRM 2011 is the latest version of Microsoft’s CRM platform.  Both the hosted (SaaS) version and on-site versions are now available for production solutions.  Unlike previous versions of Dynamics CRM, the 2011 release does NOT have a BizTalk-specific send adapter.  The stated guidance is to use the existing SOAP endpoints through the BizTalk WCF adapter.  In this post, I’ll show you what it takes to perform both “query” and “create” operations against Dynamics CRM 2011 using BizTalk Server.

Integration Strategies

Before you start, you have a choice to make.   Dynamics CRM 2011 has a diverse set of integration options (see MSDN page on Web Service integration here).  They have a very nice REST interface for interacting with standard and custom entities in the system.  BizTalk Server can’t talk “REST”, and the REST services can only be used within the Dynamics CRM application itself, so that’s not an option.  They have an ASMX endpoint for legacy clients, and that is available for BizTalk consumers.  The final option is their new WCF SOAP endpoint.  Microsoft made a distinct choice to build an untyped interface into their SOAP service.  That is, the operations like Create or Update take in a generic Entity object.  An Entity has a name and a property bag of name/value pairs that hold the record’s columns and values.  If you are a building a .NET client to call Dynamics CRM 2011, you can use the rich SDK provided and generate some early bound classes which can be passed to a special proxy class (OrganizationServiceProxy) which hides the underlying translation between typed objects and the Entity object. There’s a special WCF behavior (ProxyTypesBehavior) in play there too.  So for .NET WCF clients, you don’t know you’re dealing with an untyped SOAP interface.  For non-.NET clients, or software that can’t leverage their SDK service proxy, you have to use the untyped interface directly.

So in real life, your choice as a BizTalk developer will have to be either (a) deal with challenges of creating and consuming untyped messages, or (b) build proxy services for BizTalk to invoke that take in typed objects and communicate to Dynamics CRM.  In the future, I hope that the Microsoft team will ship a WCF behavior that could be added to the BizTalk WCF adapter that would do this typed-to-untyped translation both inbound and outbound

In this post, I’ll show option A which includes dealing directly with the bare Entity message type. 

Referencing the Service

First off, we need to add a reference to the SOAP endpoint.  Within Dynamics CRM, all the links to service endpoints can be found in the Customization menu under Developer Resources.  I’ve chosen the Organization Service which has a WSDL to point to.

http://seroter.files.wordpress.com/2011/02/2011-2-10crm01_thumb.jpg

Within a BizTalk project in Visual Studio.NET, I added a generated item and chose to consume a WCF service.  After adding the reference, I get a ton of generated artifacts.

http://seroter.files.wordpress.com/2011/02/2011-2-10crm02_thumb.jpg

Now in an ideal world, these schemas would be considered valid.  Alas, that is not the case.  When opening the schemas, a number of errors are displayed.  Apparently, there is a lot of cross-schema-referencing missing from the schemas.  You could choose to manually correct these links, or, browse to the Dynamics CRM 2011 SDK and include the provided BizTalk schemas in your project and delete the generated schemas.

For my simple demo scenario, I have a single message that holds details used for both querying and creating CRM records.  It holds the GUID identifier for a record in its Query node and in its Create node, it has a series of record attributes to apply to a new record.

http://seroter.files.wordpress.com/2011/02/2011-2-10crm03_thumb.jpg

Mapping the Query Message

Retrieving a record is pretty simple.  In this case, all you need to populate is the name of the entity (e.g. “contact”, “account”, “restaurant”), the record identifier, and which columns to retrieve.  In my map, I’ve set the AllColumns node to true which means that everything comes back. Otherwise, I’d need some custom XSLT in a functoid to populate the Columns node.

http://seroter.files.wordpress.com/2011/02/2011-2-10crm04_thumb.jpg

Mapping the Create Message

The “create” message is more complicated as we need to successfully build up a set of name/value pairs.  Let’s walk through the steps.

The first “page” of my map links the entity’s name and sets a few unused elements to null.

http://seroter.files.wordpress.com/2011/02/2011-2-10crm05_thumb.jpg

Now it gets fun. You see a node there named KeyValuePairOfstringanyType.  This node is repeated for each column that I want to populate in my created Entity.  I’m going to show one way to populate it; there are others.  On this map page, I’ve connected each source node (related to a column) to a Looping functoid.  This will allow me to create one KeyValuePairOfstringanyType for each source node.

http://seroter.files.wordpress.com/2011/02/2011-2-10crm06_thumb.jpg

Now I have to actually map the name and value across.  Let’s break this into two parts.  First, I need to get the node name into the “key” field.  We can do this by dragging each source node to the “key” field, and setting the map link’s Source Links property to Copy Name. This copies the name of the node across, not the value.

http://seroter.files.wordpress.com/2011/02/2011-2-10crm07_thumb.jpg

So far so good.  Now I need the node’s value.  You might say, “Richard, that part is easy.”  I’ll respond with “Nothing is easy.”  No, the node’s name, KeyValuePairOfstringanyType, gives it away**.** I actually need to set an XSD “type” property on the “value” node itself.  If I do a standard mapping and call the service, I get a serialization error because the data type of the “value” node is xsd:anyType and Dynamic CRM expects us to tell it which type the node is behaving like for the given column.  Because of this, I’m using a Scripting functoid to manually define the “value” node and attach a type attribute.

http://seroter.files.wordpress.com/2011/02/2011-2-10crm08_thumb.jpg

My functoid uses the Inline XSLT Call Template script type and contains the following:

<xsl:template name="SetNameValue">
     <xsl:param name="param1" />
     <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>
</xsl:template>

I also built an orchestration that calls the service and spits the result to disk, but there’s not much to that.  At this point, I deployed the solution.

Configuring the Send Port

Now within the BizTalk Admin Console, I imported one of the bindings that the WCF Service Consuming Wizard produced.  This makes life simple since there’s virtually nothing you have to change in the BizTalk send port that this binding produces.

The WCF-Custom adapter uses a custom WCF binding.

http://seroter.files.wordpress.com/2011/02/2011-2-10crm09_thumb.jpg

The only thing I added was on the Credentials tab, I added my Windows credentials for calling the service.  After creating the necessary receive port/location to pick up my initial file, send port to emit the service result to disk, and bound my orchestration, I was ready to go.

Executing the Query

In my Dynamics CRM environment, I added a customer account record for “Contoso”.  You can see a few data points which should show up in my service result when querying this record.

http://seroter.files.wordpress.com/2011/02/2011-2-10crm10_thumb.jpg

After calling the “Query” operation, I can see the result of the service call.  In reality, you’d have to build some mapping between this result and a canonical schema.

http://seroter.files.wordpress.com/2011/02/2011-2-10crm11_thumb.jpg

As for creating the record, when I send my command message in to create a new record, I see the new (Fabrikam) record in Dynamics CRM and a file on disk with the unique identifier for the new record.

http://seroter.files.wordpress.com/2011/02/2011-2-10crm12_thumb.jpg

Summary

An architect of a BizTalk Server 2011 integration with Dynamics CRM 2011 has to decide whether to use proxy components to call the CRM services, or perform the typed-to-untyped mapping exercise within the BizTalk solution.  This post showed a couple of strategies for the latter strategy.

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.