Comment unsuccessful. Please correct the errors below.

Packaging plugins

You spent years polishing your algorithm, moving bytes around, replacing strings with StringBuilders, extracting every last bit of performance from your code. The assembly Acme.CoolTools.dll is into its' 3rd major version, competitors are begging for mercy. Life is good. Then Microsoft CRM 4 comes along with its' beastly architecture wanting plug-ins to be deployed in a database. And offline CRM clients, clutching a copy of Reflector all want your dazzling assembly as well.

Perhaps you simply wanted to avoid GAC, which Chris Sells knew to be evil back in 2004. Or may be you just felt that instead of being sucked into assembly hell you would rather distribute one and only one assembly.

Whatever the real reason might be, today's challenge is to write a Microsoft CRM 4 plug-in that uses classes and methods from other assemblies; then package it all into one blob that can be deployed and distributed as required. In fact, it was the challenge from one of our customers as well as from the newsgroups (speaking of good timing!). [more]

Library code

Let's say we have a secret code that predicts annual turnover for a company based on the name. Here is our complex algorithm in its' entire glory.


using System;
namespace Acme.CoolTools
{
    public static class AccountMagic
    {
        public static decimal CalculateAnnualTurnover(string companyName)
        {
            return Math.Abs(companyName.GetHashCode());
        }
    }
}

The code is compiled into a separate assembly Acme.CoolTools.dll that is then referenced by our plug-in.

Plugin

The plug-in is as sophisticated: it's designed to "monitor" account creations and updates, and then to recalculate and update annual turnover using company name.


using System.Reflection;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.SdkTypeProxy;

[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

namespace Acme.Plugins
{
  public class AccountPlugin: IPlugin
   {
      public void Execute(IPluginExecutionContext context)
      {
          // safe-guard recursion
          if (context.Depth > 1)
              return;
          // Extract dynamic entity from postentityimage. We should have id, name and revenue
          //    properties if they are not null in the database.
          DynamicEntity entity = (DynamicEntity)context.PostEntityImages["accountEntity"];

          // Re-calculate annual turnover based on a company name. Use proven and tested algorithm.
          decimal turnover = Acme.CoolTools.AccountMagic.CalculateAnnualTurnover((string)entity["name"]);
          // Get Crm service, set revenue property and update the entity.
          ICrmService service = context.CreateCrmService(true);
          entity["revenue"] = new CrmMoney(turnover);
          service.Update(entity);
      }
   }
}

Note that we use PostEntityImages to access account information. For performance reasons this is recommended way to pass entity properties to a plug-in code instead of executing Retrieve requests left and right. As a handy side-effect, entity extracted from postentityimage is exactly what we need to update the record.

Merging the libraries

Remember how you can compile ASP.NET code into a single assembly? ILMerge is a sibling utility that can be used to merge existing assemblies into a single one. It even merges PDB files and resigns the final output. There are some GUI wrappers but, honestly, it's just as easy from the command-line; MSBuild fans can use ILMerge task.

The command to merge our two assemblies into one while maintaining strong name is quite simple:

"c:\Program Files\Microsoft\ILMerge\ilmerge" /keyfile:acme.snk Acme.Plugins.dll Acme.CoolTools.dll /out:Acme.dll

Note that we have to give it a key to resign the assembly.

Registration

Now we have a single Acme.dll assembly that needs to be registered using the latest CRM plug in registration tool:

  • Register assembly and the plugin
  • Register steps for Create and Update messages on account entity. Synchronous, Post stage, Parent Pipeline, Server. Plus Offline if required.
  • For Update message specify name as the only filtering attribute, i.e. the one that triggers plug-in execution.
  • Register post images for both steps. We only need accountid, name and revenue properties.

Registration should like the following:

image 

Summary

The code and a simple command file to build the plug-in are attached. You'll need to copy Microsoft.Crm.Sdk.dll and Microsoft.Crm.SdkTypeProxy.dll libraries into the same folder as the source files for C# compiler to find them. Alternatively, adjust references in the batch file to point to the existing location.

I has not tested Offline client but I cannot see why it would not work.

And last but not least, company named Test generates about 5.5 times annual revenue of Acme company.

AcmePlugin.zip (1.71 kb)

Posted by: George Doubinski
Last revised: 05 Dec, 2012 05:25 PM

Comments

Your Comments

Comment unsuccessful. Please correct the errors below.
Used for your gravatar. Not required. Will not be public.
Posting code? Indent it by four spaces to make it look nice. Learn more about Markdown.

Preview