Plugins in CRM 2011

When looking at Plugins in CRM 2011, it is important to break this post down into two separate sections – with a link to a follow-up third section linking to a separate post on this blog detailing how to incorporate LINQ into 2011 Plugins.

Compiling the Plugin Registration Tool ready for use with CRM 2011
Developing and Registering a new CRM 2011 Plugin
Using LINQ in CRM 2011 Plugins

Compiling the Plugin Registration Tool ready for use with CRM 2011

As with previous versions of Dynamics CRM, CRM 2011 requires the next generation of development tools to start developing and registering Plugins – so we have a few pre-requisites to take care of first:

(1) First of all you will need the .NET 4 Framework installed on your development environment, you can download this from this from here.

(2) Second we will need Visual Studio 2010 on your development environment – as this is required to open and compile the Plugin Registration Tool code that is included in the CRM 2011 SDK.

(3) Thirdly we will need the Microsoft.IdentityModel reference available to your development environment to successfully compile and run the Plugin Registration Tool – this can be done via downloading and installing the Windows Identity Foundation package from the following link.

These three steps should setup your development environment to open, compile and run the Plugin Registration Tool Solution within the CRM 2011 SDK.

Compiling the CRM 2011 Plugin Registration Tool

Compiling the CRM 2011 Plugin Registration Tool in Visual Studio 2010

Running the Plugin Registration Tool then brings up the familiar interface for adding a new Connection to a MSCRM Deployment – in my case for this post I am using a connection to a hosted instance of the CRM 2011 Beta, and so need to supply my Hosted URL and Microsoft Passport credentials to then connect to CRM.

CRM 2011 Plugin Registration Tool

Running the CRM 2011 Plugin Registration Tool and creating a Connection to either a Hosted or On-Premise deployment of CRM 2011 Beta

I have seen a few problems around this where the ‘An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail.’ error is encountered after specifying your CRM 2011 details – the following step can work to resolve this problem:

Look into your User Profile directory (C:\Users\[your user account name] in Windows 7), browse to a folder titled ‘LiveDeviceID’ and delete the LiveDevice.xml file contained within this folder. This removes any cached credentials from your workstation which may be blocking the connection between the Plugin Registration Tool and the hosted CRM 2011 Service.

Thanks to the contributors on the thread, really helped me out when hitting this error.

Once connected to our deployment of CRM 2011 – we can look at the list of Registered Plugins that we have in this deployment. Here we have the option to Register new Assemblies, Steps, Images and Service End-Points into our deployment of CRM 2011. Assemblies, Steps and Images should be familiar territory to anyone involved with CRM 4, Service End-Points are a new addition for CRM 2011 and may well form the topic of a future post, however for the moment we will be looking at registering a new Assembly to insert a simple piece of Plugin logic into CRM.

To do this our first step is to compile a Plugin Assembly for CRM 2011 – simplest way to do this initially is to again look into the CRM 2011 SDK and open one of the provided example solutions in Visual Studio 2010. The solution found at ‘.\sdk\samplecode\cs\plug-ins’ in the SDK is a good starting point:

Example SDK Plugin Solution

Example SDK Plugin Solution in Visual Studio 2010

This solution should be ready to go and so should compile into a DLL that we can then use the Plugin Registration Tool to register into CRM 2011.

Registering the Example Plugin DLL

Registering the Example Plugin DLL into CRM 2011 via the Plugin Registration Tool

Notice here we have a new option for deciding to upload the Assembly in a Sandbox mode or not – the CRM SDK contains further details on the difference between using the Sandbox or None setting here.

As with CRM 4, this will register the assembly and number of Plugins for use in CRM 2011 but not register where or when to execute these Plugins – for this we need to register a Step off the Plugin. One of the Plugins in this example solution is a simple Account Autonumber which for the code to make sense would require registration against the Account Pre-Create Event to make sense:

Registering a Step for this Plugin to operate off

Registering a new Step within CRM 2011 to invoke the example Account Number Plugin provided in the CRM 2011 SDK

The options here for registering a new Step are similar to CRM 4 with a few tweaks and differences that could form the topics of future posts.

Once registered, the list of Plugins and Steps should then resemble the following:

Plugin Registration Tool - after Registration of the Plugin and Step

Plugin Registration Tool after registering the Assembly, Plugin and Step

With the Plugin and Step registered, we should be able to browse into CRM 2011 and test that the Plugin Logic is now in effect.

As this is a simple Autonumber Plugin on the Account entity – we can add a new Account in CRM and check that the Account Number field is now being populated automatically:

Creating a new Account in CRM 2011

Creating a new Account in CRM 2011 to check the registration of the Plugin Step

Post Creation of the Account

The result in CRM 2011 as a result of the Plugin being registered on the Create Step of the Account Entity

And we can see the Plugin in affect in CRM 2011 as a result of compiling and running the Plugin Registration Tool – obviously there is a variety of new options available within the Tool for CRM 2011, but otherwise the process is similar to CRM 4. Our next step in this post is to look into how we can develop a custom Plugin for CRM 2011 that we can then register and see the affect of.

Developing and Registering a new CRM 2011 Plugin

Plugins for CRM 4 took a great idea in Callouts for CRM 3 and improved the concept immensely by allowing code to be uploaded into the Database via the Plugin Registration Tool, removing the need for saving DLL files to disk and editing XML Config files that drove Callouts. The ability to upload managed code to a remote hosted or on-premise installation of Dynamics CRM that would then fire on a specific entity event gave developers and solution architects a huge range of business logic and integration options.

Which goes some way to explain ‘not broke, don’t fix’ approach to Plugins in CRM 2011. The approach to coding and registering a Plugin is very similar in terms of Target Messages (the XML update that is happening to a record) and Pre/Post Images (the XML details describing the record before the update and after the update.

Obviously the ability to then amend other records through the CRM Webservice/API is different and involves a learning curve (particularly the LINQ approach, althrough we have seen this previously in latter releases of the CRM 4 SDK) but the underlying development patterns for Plugin Execution and Message Pipeline should be familiar to anyone used to developing Plugins against CRM 4.

If we take an example piece of business logic that we may want to implement for a client, say a simple integer field counting the number of Opportunities that have been recorded for a Customer Account, the following steps give an idea of how we could achieve this in CRM 2011:

(1) Create a new Visual Studio 2010 Class Library Project, on the assumption we are not using a Plugin Template Project.

(2) Add the required references from the CRM 2011 SDK.

Microsoft.CRM.SDK.Proxy
Microsoft.XRM.SDK

(3) Add a final required reference from the .NET Framework

System.ServiceModel
System.Runtime.Serialization

(4) In much the same fashion as CRM 4, our Plugin Assembly must be signed with a Strong Key to be deployed into a CRM 2011 Deployment.

Signing the Plugin Assembly with a Strong Key

Signing the Plugin Assembly with a Strong Key in Visual Studio 2010

(5) With these steps done we can add a new Class Library code file for our Plugin in a similar fashion to CRM 4, with a few notable differences:

The class we develop in the Plugin inherits from IPlugin interface within the SDK

public class MyPluginName : IPlugin

This then allows us to implement a single Execute method which, as per CRM 4, is the central Main() function that the Plugin executes as part of the CRM Message Pipeline.

public void Execute(IServiceProvider serviceProvider)

This takes in the servceProvider object that contains the incoming details about CRM that come from the CRM Message Pipeline – from this we can derive the Plugin Execution Context that we are familiar with from CRM 4.

IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));

From the Plugin Execution Context, we can read back the Target Message, PreMessage Images or PostMessage Images in the usual fashion as CRM 4.

if (context.PostEntityImages.Contains("PostImage") &&
    context.PostEntityImages["PostImage"] is Entity)
{
    postMessageImage = (Entity)context.PostEntityImages["PostImage"];
}
else
{
    throw new Exception("No Post Image Entity in Plugin Context for Message");
}

if (context.PreEntityImages.Contains("PreImage") &&
    context.PreEntityImages["PreImage"] is Entity)
{
    preMessageImage = (Entity)context.PreEntityImages["PreImage"];
}
else
{
    throw new Exception("No Pre Image Entity in Plugin Context for Message");
}

if (context.InputParameters.Contains("Target") &&
  context.InputParameters["Target"] is Entity)
{
    targetMessageImage = (Entity) context.InputParameters["Target"];
}
else
{
    throw new Exception("No Target Message Entity in Plugin Context for Message");
}

As per CRM 4, we can also use the Plugin Execution Context to obtain a limited reference to the CRM Webservice such that we can make Retrieve, Create or Update requests to CRM – the code here is slightly different from CRM 4 but the concept is similar:

IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));

IOrganizationService service = factory.CreateOrganizationService(context.UserId);

Beyond these points, the process of developing a Plugin for CRM 2011 can then use the same structure as CRM 4 – the most notably change being the underlying changes to how Webservice Requests are communicated to the CRM Webservice, however these are documented or in the process of being documented in the CRM 2011 SDK and may well form the subject of a further post on CRM 2011. An obvious change in the code below is the concept of the EntityReference property instead of CRM 4’s Lookup, Owner or Customer property types.

The following table is a brief breakdown of how this changes reading back Lookup properties from a Plugin Image:

CRM 4 CRM 2011
Microsoft.Crm.Sdk.Key opportunityid = (Microsoft.Crm.Sdk.Key) postImage.Properties["opportunityid"];

Guid opportunityRecordId = opportunityid.Value;
EntityReference opportunityid = (EntityReference) postImage.Attributes["opportunityid"];

Guid opportunityRecordId = opportunityid.Id;
Microsoft.Crm.Sdk.Customer customerid = (Microsoft.Crm.Sdk.Customer)postImage.Properties["customerid"];

string customerRecordType = customerid.type;
Guid customerRecordId = customerid.Value;
EntityReference customerid = (EntityReference) postImage.Attributes["customerid"];

string customerRecordType = customerid.Type;
Guid customerRecordId = customerid.Id;

(6) From these points we can develop our Plugin Logic to work from within CRM 2011 – the following code describes a very basic Plugin to count Opportunities that are linked to an Account.

using System;
using System.ServiceModel;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;

public class OpportunityCountPlugin : IPlugin
{
    const string OPPORTUNITYCOUNT_DATABASENAME = "new_opportunitycount";

    public void Execute(IServiceProvider serviceProvider)
    {
        try
        {
            // Obtain the execution context from the service provider.
            IPluginExecutionContext context =
                (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));

            Entity opportunityEntityImage;

            #region Get Opportunity Entity Image
            if (context.MessageName == "Create")
            {
                // use Post Image for Create Messages
                if (context.PostEntityImages.Contains("PostImage") &&
                    context.PostEntityImages["PostImage"] is Entity)
                {
                    opportunityEntityImage = (Entity)context.PostEntityImages["PostImage"];
                }
                else
                {
                    throw new Exception("No Post Image Entity in Plugin Context for Create Message");
                }
            }
            else if (context.MessageName == "Delete")
            {
                // use Pre Image for Delete Messages
                if (context.PreEntityImages.Contains("PreImage") &&
                    context.PreEntityImages["PreImage"] is Entity)
                {
                    opportunityEntityImage = (Entity)context.PreEntityImages["PreImage"];
                }
                else
                {
                    throw new Exception("No Pre Image Entity in Plugin Context for Delete Message");
                }
            }
            else
            {
                // Plugin not valid for any other Message Types
                throw new Exception("OpportunityCountPlugin Plugin is not valid for the '" + context.MessageName + "' message type");
            }

            if (opportunityEntityImage.LogicalName != "opportunity")
            {
                // plugin is valid for this entity
                throw new Exception("OpportunityCountPlugin Plugin is not valid for the '" + opportunityEntityImage.LogicalName + "' entity type");
            }
            #endregion

            #region Plugin Business Logic

            // interrogate Opportunity Post Image XML to determine the 'customerid' field
            if (opportunityEntityImage.Attributes.Contains("customerid"))
            {
                EntityReference customerid = (EntityReference)opportunityEntityImage.Attributes["customerid"];

                if ((customerid.LogicalName == "account") && (!( customerid.Id.Equals(Guid.Empty) )))
                {
                    // Get a reference to the Organization service.
                    IOrganizationServiceFactory factory =
                        (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));

                    // Local variable for reference to CRM Service - as Plugins should be developed as stateless
                    // due to CRM 2011 Caching Plugin object instances
                    IOrganizationService service = factory.CreateOrganizationService(context.UserId);

                    int opportunities = GetOpportunityCount(customerid.Id, service);

                    UpdateOpportunityCount(customerid.Id, opportunities, service);
                }
            }

            #endregion
                
        }
        catch (Exception ex)
        {
            // Handle general exception
            throw new InvalidPluginExecutionException("CRM2011.Example.OpportunityCountPlugin --> Execute '" + ex.Message + "'");
        }
    }

    #region Plugin CRM Methods
    /// <summary>
    /// Returns a simple count of the number of Opportunities associated to an Account
    /// </summary>
    /// <param name="accountId">The GUID Id of the Account involved</param>
    /// <param name="service">The CRM 2011 Organization Service reference to retrieve the data from</param>
    /// <returns>The current number of Opportunities associated directly with the Account</returns>
    public int GetOpportunityCount(Guid accountId, IOrganizationService service)
    {
        try
        {
            QueryByAttribute queryByAttribute = new QueryByAttribute("opportunity");
            queryByAttribute.ColumnSet = new ColumnSet(new string[] { "opportunityid" });
            queryByAttribute.Attributes.Add("customerid");
            queryByAttribute.Values.Add(accountId);

            EntityCollection retrieved = service.RetrieveMultiple(queryByAttribute);

            return retrieved.Entities.Count;
        }
        catch (FaultException<OrganizationServiceFault> exceptionServiceCall)
        {
            throw new Exception("GetOpportunityCount [OrganizationServiceFault '" + exceptionServiceCall.Message + "']");
        }
        catch (Exception ex)
        {
            throw new Exception("GetOpportunityCount [GeneralException '" + ex.Message + "']");
        }
    }

    /// <summary>
    /// Updates a custom Opportunity Count field on the specified Account to a new integer value
    /// </summary>
    /// <param name="accountId">The GUID Id of the Account involved</param>
    /// <param name="opportunityCount">The new integer value to update the Opportunity Count field to</param>
    /// <param name="service">The CRM 2011 Organization Service reference to update to</param>
    public void UpdateOpportunityCount(Guid accountId, int opportunityCount, IOrganizationService service)
    {
        try
        {
            Entity updateAccount = new Entity("account");
            updateAccount.Attributes.Add("accountid", accountId);
            updateAccount.Attributes.Add(OPPORTUNITYCOUNT_DATABASENAME, opportunityCount);

            service.Update(updateAccount);
        }
        catch (FaultException<OrganizationServiceFault> exceptionServiceCall)
        {
            throw new Exception("UpdateOpportunityCount [OrganizationServiceFault '" + exceptionServiceCall.Message + "']");
        }
        catch (Exception ex)
        {
            throw new Exception("UpdateOpportunityCount [GeneralException '" + ex.Message + "']");
        }
    }
    #endregion
}

(7) With the code developed and compiled we can then register the Plugin into our deployment of CRM 2011 (currently Beta) to insert the developed Business Logic. In the simple Opportunity Count example above, this would mean registering the Plugin on Create or Delete of the Opportunity entity with associated Post or Pre Image.

Registering a Custom Plugin into CRM 2011

Registering the Example Opportunity Count Plugin into CRM 2011

(8) With this done we should be able to see the Plugin in action – this Plugin fires whenever we delete or create an Opportunity that is attached to an Account, so we can add several Opportunities to a particular Account as way of seeing the ‘new_opportunitycount’ field update accordingly:

Opportunity Count Plugin in action

List of three Opportunities associated to a particular Account in CRM 2011

Opportunity Count Plugin in action

Resulting field in CRM 2011 automatically counting the 3 Opportunities associated to the Account

Obviously this Plugin in itself is slightly flawed as the logic does not account for Opportunities that have a Contact linked as the Customer, and realistically a Report or Chart would be better placed to show a running tally of Opportunities per Account. However hopefully this gives an initial example of how developing a Plugin for CRM 2011 may differ from Plugins in CRM 4.

From experience of CRM 3 and 4 Projects Plugins and Callouts are often the most popular method for advanced customisation of Dynamics CRM as, whilst firmly in the Development camp as opposed to Functional Customisation, Plugins can be simply developed and deployed as re-usable components into any CRM deployment that meets the Plugin dependencies for fields and entities.

For adding LINQ Queries into CRM 2011 Plugins, the following post acts as a follow-up and may be helpful: Using LINQ in CRM 2011 Plugins

Advertisements
This entry was posted in CRM 2011, Development. Bookmark the permalink.

21 Responses to Plugins in CRM 2011

  1. Pingback: CRM 2011 | CRM Consultancy Blog

  2. Pingback: Tweets that mention Plugins in CRM 2011 | CRM Consultancy Blog -- Topsy.com

  3. Pingback: Did you know, Dynamics CRM & xRM #5 « North 52

  4. I got an error trying to connect PluginRegistration to my CRM OnLine ans I found the solution in your article. Thank you very much for your help.

  5. Pingback: Overview CRM 2011 | Onestopdynamics

  6. Hooman says:

    Thanks for the article, it was great, a part of the code for obtaining the the count of the opportunities linked to the account is missing.

  7. Very useful. Thanks for sharing.

  8. I have created a plugin registration tool that runs from a command line for Microsoft Dynamics CRM 2011 here: http://pluginregcrm2011.codeplex.com/

  9. Since this post was originally published, a new page has been added to the CRM SDK which might be useful when compiling and using the Plugin Registration Tool:

    http://msdn.microsoft.com/en-us/library/hh237515.aspx

  10. Pingback: CRM 4 – Workflows, Custom Workflows ou Plugins | Tiago Michelini Cardoso

  11. Pingback: CRM2011 Plugins, Pre-image and post-image -Kevin's Mocha

  12. Pingback: CRM 2011 Online Plugin Registration Tool Connection Error | shoaibfaruq

  13. GuangmingHe says:

    do you guys know anything about vs 2010 template for dynamics crm plugin?

    There is constructor: internal Plugin(Type childClassName)
    {
    this.ChildClassName = childClassName.ToString();
    }

    How this get called? when childClassName gets passed?

    thanks,

  14. Thanks for the post ! This was a good starting point for me !

  15. Alexandre says:

    Very Good. Helped me a lot. Thanks

  16. paritoshmmmec says:

    very useful information thanks

  17. ekoncis says:

    Thanks very good post
    Read my article about custom plugin in CRM
    2011 http://ekoncis.blogspot.com/2012/01/crm-2011-custom-email-with-pdf-report.html

  18. Jefferson says:

    As I web-site possessor I believe the content material here is
    rattling fantastic , appreciate it for your hard
    work. You should keep it up forever! Best of luck.

  19. rhscyz.com says:

    It’s really a cool and helpful piece of info. I am happy that you shared this useful information with us. Please keep us informed like this. Thanks for sharing.

  20. I have been browsing online more than 2 hours today, yet I never found any interesting article like yours.

    It’s pretty worth enough for me. Personally, if all web owners and bloggers made good content as you did, the internet will be much more useful than ever before.

  21. punditor says:

    Hi, thanks for the great article about Plugins in CRM. Very helpful.
    Thanks

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s