Introduction
This article discusses about a tool that helps to test and debug the Microsoft Dynamics CRM 4.0 (MSCRM4.0) plug-in. The tool can also help a MSCRM4.0 developer to explore the context a plug-in will receive for a message the developer has never worked before, thus giving the developer an idea of what is contained in the context for a particular message of a particular entity. The utility consists of a serializing plug-in and a plug-in test bed.
Background
Serialization plays an important role. We use serialization to capture the plug-in's context from the server. The serializing plug-in code has serialization logic to serialize the contents of the context provided by Microsoft Dynamics CRM 4.0 during plug-in execution.
Serializing Plug-in Code
The complete code of the serializing plug-in is given below. It serializes the context it receives. This plug-in has to be registered once.
public class SerializingPlugin : IPlugin
{
public void Execute(IPluginExecutionContext context)
{
XmlSerializer formatter = new XmlSerializer
(typeof(MyPluginContext), string.Empty);
MyPluginContext myContext = new MyPluginContext();
myContext.Copy(context);
string filename = string.Format(@"C:\windows\temp\{0}-{1}",
context.PrimaryEntityName, context.MessageName);
string suffix = string.Empty;
int i = 0;
while (File.Exists(filename + suffix))
{
suffix = i.ToString();
i++;
}
FileStream file = File.Create(filename + suffix);
formatter.Serialize(file, myContext);
file.Flush();
file.Close();
}
}
How It Works
Let's assume we have to debug a plug-in which executes on "Post" "Create" of "account". The serializing plug-in has to be registered, and a step has to be created for the "Create" message of "account" entity. Then create an account with the desired values. The serializing plug-in executes and creates an XML file in C:\windows\temp which contains the context it received. Provide this file as an input to the TestPlugin application.
TestPlugin Application
The TestPlugin
application helps testing/debugging a plug-in using the Visual Studio debugger. The application takes the CRM user credentials of the same CRM instance where the serializing plug-in is deployed. The path of the serialized IPluginExecutionContext
and the path of the plug-in DLL to be debugged have to be provided by clicking the LoadSearializedContext and LoadAssembly buttons, respectively. Finally, after providing the fully qualified class name (i.e., class name with namespace) of the plug-in class, click on the "Start" button to start the plug-in execution.
Set the breakpoint in the pluginClass.Execute(myPluginContext);
line in the btnStart_Click
event handler. When the debugger hits the location, step into the line. Make sure the .pdb file is also present in the same folder as the plugin DLL.
Sample Serialized IPluginExecutionContext
<MyPluginContext xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<BusinessUnitId>bada4721-db0f-4318-8471-ed85981c94da</BusinessUnitId>
<CallerOrigin xmlns:q1=http://schemas.microsoft.com/crm/2007/CoreTypes
xsi:type="q1:ApplicationOrigin" />
<CorrelationId>0d9b8e39-7b1e-4590-a03e-535464362ebd</CorrelationId>
<CorrelationUpdatedTime>2010-06-26T19:08:57Z</CorrelationUpdatedTime>
<Depth>1</Depth>
<InitiatingUserId>a6c3ac10-2fc6-41a8-b24e-3b9213cfcc56</InitiatingUserId>
<InputParameters>
<Properties>
<PropertyBagEntry>
<Name>Target</Name>
<DynamicEntity Name="account">
<Properties xmlns="http://schemas.microsoft.com/crm/2006/WebServices">
<Property xsi:type="StringProperty" Name="name">
<Value>TestAccount</Value>
</Property>
<Property xsi:type="LookupProperty" Name="transactioncurrencyid">
<Value type="transactioncurrency">
0ad13e34-ad80-de11-a371-00155d625206</Value>
</Property>
<Property xsi:type="OwnerProperty" Name="ownerid">
<Value type="systemuser">81c63db2-7bee-4483-aa8b-dc179af8ac1f</Value>
</Property>
<Property xsi:type="CrmBooleanProperty" Name="creditonhold">
<Value>false</Value>
</Property>
<Property xsi:type="PicklistProperty" Name="preferredcontactmethodcode">
<Value name="Any">1</Value>
</Property>
<Property xsi:type="CrmBooleanProperty" Name="donotemail">
<Value>false</Value>
</Property>
<Property xsi:type="CrmBooleanProperty" Name="donotbulkemail">
<Value>false</Value>
</Property>
<Property xsi:type="CrmBooleanProperty" Name="donotphone">
<Value>false</Value>
</Property>
<Property xsi:type="CrmBooleanProperty" Name="donotfax">
<Value>false</Value>
</Property>
<Property xsi:type="CrmBooleanProperty" Name="donotpostalmail">
<Value>false</Value>
</Property>
<Property xsi:type="CrmBooleanProperty" Name="donotsendmm">
<Value>false</Value>
</Property>
<Property xsi:type="CrmBooleanProperty" Name="donotbulkpostalmail">
<Value>false</Value>
</Property>
<Property xsi:type="CrmBooleanProperty" Name="isprivate">
<Value>false</Value>
</Property>
<Property xsi:type="PicklistProperty" Name="shippingmethodcode">
<Value>1</Value>
</Property>
<Property xsi:type="PicklistProperty" Name="address2_shippingmethodcode">
<Value>1</Value>
</Property>
<Property xsi:type="PicklistProperty" Name="address2_addresstypecode">
<Value>1</Value>
</Property>
<Property xsi:type="PicklistProperty" Name="accountratingcode">
<Value>1</Value>
</Property>
<Property xsi:type="CrmBooleanProperty" Name="participatesinworkflow">
<Value>false</Value>
</Property>
<Property xsi:type="PicklistProperty" Name="territorycode">
<Value>1</Value>
</Property>
<Property xsi:type="PicklistProperty" Name="customersizecode">
<Value>1</Value>
</Property>
<Property xsi:type="PicklistProperty" Name="address2_freighttermscode">
<Value>1</Value>
</Property>
<Property xsi:type="CrmBooleanProperty" Name="merged">
<Value>false</Value>
</Property>
<Property xsi:type="PicklistProperty" Name="accountclassificationcode">
<Value>1</Value>
</Property>
<Property xsi:type="PicklistProperty" Name="businesstypecode">
<Value>1</Value>
</Property>
</Properties>
</DynamicEntity>
</PropertyBagEntry>
<PropertyBagEntry>
<Name>OptionalParameters</Name>
<ArrayOfOptionalParameter>
<OptionalParameter xmlns:q2=
http://schemas.microsoft.com/crm/2007/WebServices
xsi:type="q2:CreateDuplicatesOptionalParameter">
<q2:Value>false</q2:Value>
</OptionalParameter>
</ArrayOfOptionalParameter>
</PropertyBagEntry>
</Properties>
</InputParameters>
<InvocationSource>0</InvocationSource>
<IsExecutingInOfflineMode>false</IsExecutingInOfflineMode>
<MessageName>Create</MessageName>
<Mode>0</Mode>
<OrganizationId>10f25dce-ac80-de11-a371-00155d625206</OrganizationId>
<OrganizationName>MSCRMORG</OrganizationName>
<OutputParameters>
<Properties>
<PropertyBagEntry>
<Name>id</Name>
<anyType xmlns:q3=http://microsoft.com/wsdl/types/
xsi:type="q3:guid">4785e644-5681-df11-a9e8-00155d625206</anyType>
</PropertyBagEntry>
</Properties>
</OutputParameters>
<PostEntityImages>
<Properties />
</PostEntityImages>
<PreEntityImages>
<Properties />
</PreEntityImages>
<PrimaryEntityName>account</PrimaryEntityName>
<SecondaryEntityName>none</SecondaryEntityName>
<SharedVariables>
<Properties>
<PropertyBagEntry>
<Name>DefaultsAddedFlag</Name>
<anyType xsi:type="xsd:boolean">true</anyType>
</PropertyBagEntry>
<PropertyBagEntry>
<Name>ValidationOccurredFlag</Name>
<anyType xsi:type="xsd:boolean">true</anyType>
</PropertyBagEntry>
</Properties>
</SharedVariables>
<Stage>50</Stage>
<UserId>81c63db2-7bee-4483-aa8b-dc179af8ac1f</UserId>
</MyPluginContext>
Advantages
- Facilitates Test Driven Development of MSCRM4.0 plug-ins
- Production server plug-in issues can be simulated
- Production environment troubleshooting and debugging
- Developer need not maintain CRM instances for developing and testing plug-ins
Points of Interest
I learnt the importance of Interface and how an Interface hides the implementation details and supports extensibility. The IPluginExecutionContext
is an excellent example. XmlSerialization
is an amazing concept!
History
- 26 June, 2010: Initial post
- 28 June, 2010: Added
MetadataService
support
- 16 July, 2010: Added
TestPlugin
application's fields save and restore for repeated testing
- 23 July, 2010: Added
TestPlugin
application's fields save and restore for repeated testing - updated source code