If you’re a beginner, or maybe even somewhat experienced, CRM developer doing a server side plug-in, or in particular, a custom workflow activity for the first time, there is a possibility that it goes like this.
You plan out what needs to happen, code and test your routine running as a client. Then satisfied with the result, you implement your code into a plug-in or custom workflow activity template, register your assembly to the CRM server, and try to have it execute, only to have it fail with this:
Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly ‘Microsoft.Xrm.Client, Version=18.104.22.168, Culture=neutral, PublicKeyToken=31bf3856ad364e35’ or one of its dependencies. The system cannot find the file specified.
You might wonder if the wrong DLL versions are being referenced, or if the server is not set up correctly? Searching online will eventually lead to the answer. This error is expected because client extensions, by design, are not part of a CRM server installation.
At this, you might ponder re-doing your code. You may try to work out if it is safe to cram this assembly in the global assembly cache of the server, and if you do, how you’ll go about the logistics of deploying that.
There is however a third option to avoid the trouble of either, provided the CRM server is on-premises. You can “merge” the Microsoft.Xrm.Client assembly into yours, allowing it to deploy inside your assembly and be available for your code to use.
Sure, it might sound like some kind of hack, but it is possible, and the utility to do it is ILMerge. Written by Mike Barnett of Microsoft Research, this utility is sanctioned by Microsoft and available for download from the Microsoft website as a standalone install or inside Visual Studio using NuGet. It is a small but powerful tool that takes an assembly, referred to as the primary assembly, and merges one or more other .NET assemblies into it.
ILMerge is command line executable with numerous command options, but the most basic of merge operations can be done like this:
ILMerge.exe /t:Library /out: “MyCRMPlugIn.DLL” “Microsoft.Xrm.Client” /keyfile:MyStrongNameKey.snk
The first assembly of the “out” parameter is the “primary assembly” of which any that follow are merged into. The “keyfile” parameter provides the strong name key on which to re-sign your assembly and is strongly recommended for any production code to guarantee authorship.
The only code changes necessary would be if you have a test program referencing your plug-in, in which the test program will show compiler errors for namespace ambiguity because there will be two instances of the Microsoft.Xrm.Client namespace present. One from the reference and the other merged into your assembly. This can be easily worked around using the aliases as follows:
- In your test program set the property of the Xrm.Client reference to a new name, for this example we’ll use XrmClient.
- Add the extern alias XrmClient declaration to the top of any application code files.
- Reference any Xrm.Client types using the alias name with the :: namespace alias qualifier. For example XrmClient::Microsoft.Xrm.Client.CrmConnection to reference the CrmConnection type.
Wait, surely there must be some limitations to this? After all Microsoft.Xrm.Client isn’t part of CRM server for a reason.
Yes, some parts of Microsoft.Xrm.Client touch .NET Framework components that reference things such as the Windows registry for information which means any CRM server plug-in using it cannot run in sandbox isolation mode (the isolation mode must be set to None), and thus is only allowable with on-premises CRM installations.
This seems like a hassle. Why should I bother coding with Microsoft.Xrm.Client?
Using the Microsoft.Xrm.Client library provides the worth benefit of being able to use the CrmOrganizationServiceContext class to query, manage, and track objects with less code. This has the obvious benefits of less development time and fewer chances of bugs. In addition, this library has useful support for deep cloning of entities, generation of relationship objects from schema names, and JSON serialization of Entity objects.
Does this mean I can now break out my organizations shared CRM plug in code into its own class library project?
Absolutely, and this has no effect on sandbox isolation. If you are not using Microsoft.Xrm.Client, and all your assemblies can function in CRM sandbox isolation, then your merged assembly will be able to do as well.
Visual Studio has problems with ILMerge running in a build event? Is there any way to make this work automatically?
Yes, there is a NuGet package called MSBuild.ILMerge.Task by Alexander Nosenko which automates merging of referenced assemblies during compile based on the CopyLocal setting.