A CRM MVP, we welcome our guest blogger David Jennaway who is the technical director at Excitation .
In Microsoft Dynamics CRM 4.0, plugins offer a very powerful mechanism to attach code to a wide range of platform events. Each event is identified by the message that was used to cause the event. However, the very range of messages available can cause confusion, as it is not always obvious which message to use for a given operation.
In this article I’ll describe a way to identify the messages and associated data for any platform. This uses 2 components; the main one comprises extensions to the Plugin Developer tool that provide a simple way to attach a plugin to all message permutations, or a designated set of messages. The other component is a sample plugin to log message details to a SQL database. Both components are implemented as .Net assemblies, and I’ve posted the source code to each on the MSDN Code Gallery. See the links later in the article
Plugin Message and Entities
The Microsoft Dynamics CRM 4.0 SDK contains a list of all message and entity combinations that are permitted on the parent pipeline. You could use this information to register for each combination, but there is an easier way.
All the supported message and entity combinations are stored within the MSCRM database, in the sdkmessagefilter entity. This contains the following attributes:
- sdkmessageid. This is a lookup to the sdkmessage entity which contains an entry for each message
- primaryobjecttypecode. An EntityNameReference for the primary entity
- secondaryobjecttypecode. An EntityNameReference for the secondary entity
- availability. An enumeration that identifies if this combination is available on the client, server or both
- iscutomprocessingstepallowed. A boolean field that specifies whether you can register a custom plugin for this combination
The way I use this information is to extend the PluginDeveloper tool that is included in the Microsoft Dynamics CRM 4.0 SDK. Within the register.xml file I’ve added support for a BulkStep element; this allows registration of a plugin against all message combinations, with optional filtering. The following example shows how to register for all messages on the post event of the parent pipeline.
CustomConfiguration = "<Config ConnectionString="Data Source=CRM;Initial Catalog=PlugInLogger;Integrated Security=SSPI" ThrowException="False" />"
Description = "Test Post Event"
FilteringAttributes = ""
ImpersonatingUserId = ""
InvocationSource = "0"
Mode = "0"
PluginTypeFriendlyName = "PlugInLogger"
PluginTypeName = "PlugInMessageLogger.PlugInLogger"
Stage = "50"
The core of the code is as follows. It uses a query against the sdkmessagefilter entity to find the appropriate messages.
The Plugin Logger
This plugin is one example of how to capture plugin data. I use a couple of SQL tables to record the plugin event, as this makes for easy analysis via SQL queries. The two SQL tables are as follows:
- PlugInEvent. This contains one record for every event, and includes the time, the message, stage and primary and secondary entities
- PlugInParameter. This contains one record for every item in a PropertyBag that is passed to the event – i.e. each InputParameter, OutputParameter, PreEntityImage, PostEntityImage and SharedVariable. Each record contains an XML serialisation of the property data
These tables can be created with the following script:
CREATE TABLE [dbo].[PlugInEvent](
[EventID] [int] IDENTITY(1,1) NOT NULL,
[EventDate] [datetime] NOT NULL,
[Message] [nvarchar](64) NOT NULL,
[PrimaryEntity] [nvarchar](64) NULL,
[SecondaryEntity] [nvarchar](64) NULL,
[Pipeline] [nvarchar](10) NOT NULL,
[Stage] [int] NOT NULL
CREATE TABLE [dbo].[PlugInParameter](
[EventID] [int] NOT NULL,
[ParamName] [nvarchar](64) NULL,
[ParamDirection] [nvarchar](10) NULL,
[ParamType] [nvarchar](256) NULL,
[ParamXml] [ntext] NULL
The plugin expects a configuration parameter in the constructor. This contains 2 pieces of information; the SQL connection string to the database that contains the above tables, and a flag indicating whether to throw any exceptions during execution. As a plugin can only take one string configuration parameter, I pass this data as an encoded XML document.
In addition to the message registration covered in the previous section, you’ll need the following steps to install and use this plugin:
- Create a SQL database for the PlugInEvent and PlugInParameter tables
- Use the SQL script included in the release package in the MSDN Code Gallery to create the PlugInEvent and PlugInParameter tables
- Assign appropriate SQL permissions on the database and tables. The plugin runs under the Active Directory context of the identity of the CrmAppPool application pool. This account will need Insert permission on both tables. Note that, if the database is on a different server from the CRM platform, then in-built accounts (Network Service, Local System, and Local Service) are identified by the Active Directory machine account
- Identify the connection string to use for the database. You’ll need this for the message registration
Additional Comments and Usage Notes
One web service call is made to create all the message registrations. This can potentially take several minutes, so it may be necessary to increase the Timeout on the web service proxy from the default of 100 seconds.
A separate BulkStep element is required for the pre and post events, and the child and parent pipelines, so a total of 4 BulkStep elements is required to register for every supported event.
The child pipeline only supports simple messages. These are Create, Update, Delete and RetrieveExchangeRate. The extension code described here is hard-coded to limit registration on the child pipeline to these messages.
- Source code for Plugin developer extensions: http://code.msdn.microsoft.com/crm40pluginbulk
- Source code for PluginLogger: http://code.msdn.microsoft.com/crm40pluginlogger
- Sdkmessagefilter class in the CRM 4.0 SDK: http://msdn.microsoft.com/en-us/library/bb957872.aspx