Monday, April 11, 2016

I recently had to reference the Newtonsoft.Json dll from
within a CRM plugin and run into the infamous problem that there is no real
support for referenced assemblies from custom plugins or workflow activities.
This post provides a workaround using ILMerge.

The Problem

This has been a feature gap since plugins were introduced in
CRM, you often want to reference external dll libraries from within your
plugin. First, let’s look at how the CLR (.Net) loads referenced assemblies in
order to understand the problem a little better. During runtime, what happens
with referenced assemblies is that the CLR will try to load the referenced
assemblies from some predetermined locations. For example, it will try to
locate and load that assembly from the GAC or from the same directory as where
the current process is executing. When you compile an assembly and it has other
references, what happens is that the referenced assemblies get dropped in the
same bin folder as your compiled assembly so they can be easily located and loaded
at runtime.

That works great, and in CRM there is also the concept of
the “bin” folder but the usage of it is a bit deprecated or not recommended
(see ….). So in theory if you have access to the CRM server you could drop all
your referenced assemblies in the “assembly\bin” folder or you can install your
referenced assemblies in the server GAC and ten they will be able to be loaded
at runtime. However, this is a big problem because you don’t always have access
to the server and because depending on a specific server is risky as your
servers might change, there could be a load balancer with multiple servers,
etc.

So what else can you do if you have to register your plugin
in the database? Well, when you register your plugin in the database what
happens is that CRM will deserialize your dll content and load your plugin type
explicitly, but you have no way of telling CRM that it must also load another
assembly and you have no way to upload a reference assembly to the database.
Certainly all .Net native assemblies like System.dll can be loaded because you
can assume .Net to be available from all CRM servers, but what if your
reference assembly is a third party like Newtonsoft.Json.dll ? In cloud
environments (plugins in sandbox mode), reference assemblies can be dangerous
because you do not know what code they are running in your shared cloud
servers, so this probably explains why there is no support for reference
assemblies in CRM. For plugin assemblies this risk is easily mitigated by running
your plugin under partial trust in the CRM sandbox which uses code access
security, .Net framework’s way of allowing external code to execute in a safe
manner. This is why your plugins would fail to do things like try to read a
local file or shut down the current server (you wouldn’t expect that to work in
cloud environments!).

The Solution

So at this point you are left with 2 options: You can either
compile the source code of your reference assemblies into your plugin assembly
(if you have the source code) or you can ILMerge the reference assembly with
your plugin assembly. In this example I will focus on the second option and I
will illustrate exactly how I did it for the example of Newtonsoft.Json.dll in
the following 3 simple steps:

1.Add ILMerge Nuget to your plugins project

2.Add Newtonsoft.Json Nuget package to your
plugins project

3.Now you simply need to edit your project
configuration in Visual Studio such that when you build your plugin it
automatically merges it with your reference assemblies (so you will never have
to manually do ilmerge). To do this go to the “Build Events” tab of your
project settings and under “Post Build event command line” box enter the
following:

Note you might need to modify the command above
according to the version of the Nuget package you installed in previous step
and you have to specify the location of your SNK file that you use to sign your
plugin assembly.

Now when you build your plugin assembly, the
Newtonsoft.Json.dll is embedded inside your plugin so CRM will not need to load
the reference assembly. There you go, I hope you find this useful and simple
enough.

About me

I used to be part of the CRM product team at Microsoft and I recently joined the Avanade Canada team as a consultant in the CRM Service Line, I developed a sort of delight in seeing how Dynamics CRM is evolving as a platform (xRM) and how its feature set has matured to be able to model and address plenty of business scenarios beyond Customer Relationship Management. I was recently given the Microsoft MVP award in Dynamics CRM and I am really excited to be part of the program and continue contributing to the CRM community.