Search

Antony Ellis Dynamics 365 CRM

The blog where Microsoft Dynamics 365 knowledge is shared and development made easier

Tag

ILMerge

How to register an early bound library within Microsoft Dynamics CRM Online Sandbox Isolation Mode (Part 2)

Welcome to Part 2 on is subject. If you haven’t already read the How to register an early bound library within Microsoft Dynamics CRM Online Sandbox Isolation Mode (Part 1) I highly recommend you do so as it will give you added context along with some of the issues and options when deciding whether ILMerge is the best approach or not.

This article will outline how to setup ILMerge so that when you compile the various projects within Visual Studio the output assemblies can be merged into a single assembly as required when registering those as Plugins/Custom Workflow libraries to be used with Microsoft Dynamics CRM Online or On Premise where the isolation mode is set to Sandbox.

How to use ILMerge

There are two ways of doing this:

  • As part of the MS Build process
  • Manually through the command line

The following instructions are based on Visual Studio 2012/.NET 4.5.2 it should more or less be the same for the latest versions of Visual Studio.

Using ILMerge as part of the build process

This is a little tricky to setup at first but once done means that whenever you build your projects as part of the CRM solution everything will be built (merged) for you without you then having to do additional steps manually.

The first thing you need to do is right click on your plugin and workflow projects and go to Manage NuGet Packages. In the top right search for ILMerge. Then install the ILMerge and MSBuild ILMerge Task. The packages will be downloaded and will create a folder called packages within your solution folder. Download ILMerge Task first and allow it to download the latest ILMerge.

Untitled.png

You can then verify everything looks as expected by go to Tools->Library Package Manager->Package Visualiser:

Untitled.png

Now we need to ensure that ILMerge command is part of the build process to do this we must manually edit the Plugin cs file. Unfortunately the package installer adds files to our solution but does not go all the way in configuring this for us. You will note the following files are shown in your project now:

  • ILMerge.props
  • Packages.config
  • ILMergeOrder.txt

Right-click on your plugin project and near the bottom of the menu context window you will see an option “unload project”. This enables you to edit the project file within VS simply by right clicking and selecting edit.

Set the paths and make sure the settings within packages config file matches. You need to ensure paths match your VS solution file structure and that the versions of ILMerge Task match those from the installed NuGet versions.

  =”..\packages\MSBuild.ILMerge.Task.1.0.2\build\MSBuild.ILMerge.Task.props” Condition=”Exists(‘..\packages\MSBuild.ILMerge.Task.1.0.2\build\MSBuild.ILMerge.Task.props’)” />

Then also near the bottom of the file:

 This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.

=”..\packages\MSBuild.ILMerge.Task.1.0.2\build\MSBuild.ILMerge.Task.targets” Condition=”Exists(‘..\packages\MSBuild.ILMerge.Task.1.0.2\build\MSBuild.ILMerge.Task.targets’)” />

<!– To modify your build process, add your task inside one of the targets below and uncomment it. Other similar extension points exist, see Microsoft.Common.targets.

After this you then right-click on the project to “reload the plugin project”. This completes editing the Microsoft build steps for your specific CRM solution project however we need to also update the targets.

Assembly Type Information

When I first tried to use ILMerge I forgot to do this step and you guessed it things didn’t work as I had expected. Looking back now it seems an obvious mistake and it probably was but there are so many things to consider when building a project for CRM deployment it’s easy even for the most experienced to forget key steps.

Sufficed to say this is major step for the assemblies to work as you would expect. If you don’t do it then serialisation errors will be triggered; there are work around’s such as defining implicitly the types and converting them at compile time. The type of error from CRM which I am talking about would look as follows:

System.Runtime.Serialization.SerializationException: Microsoft Dynamics CRM has experienced an error. -2147220970

Which according to Microsoft basically translates to “An unexpected error occurred”. If you debug, you are likely to get misleading errors such as “incoming entity id Does Not Exist”.

Go into your Workflow Activity or Plugin folder and expand the “Properties” node to reveal the file “AssemblyInfo.cs”.

Untitled.png

Then at the very bottom of that section you need to add the following line:

[assembly: Microsoft.Xrm.Sdk.Client.ProxyTypesAssemblyAttribute()]

This will ensure that SDK Class Types are packaged up as part of the DLL, this is important so that when it’s merged, the CRM application is able to Cast between your early bound types and the types  expected within the proxy. For example _service.Create(Entity TaskObj), the Create method expects an “Entity” class type not an early bound Task type.

If you don’t have this line all plugins and custom workflow activity projects registered in Sandbox isolation mode will not work and throw a serialization error. One possible work around is to change the code to say _service.Create(myEarlyBoundObject.ToEntity()) that would work however is not exactly very readable and is something of a hack. To avoid the problem add the line above to the AssemblyInfo.cs files wherever those projects need to reference your early bound library.

 

Editing Your Packages.config

This is important as its a clear sign you have ILMerge in the right VS Project simply by it being present. More importantly the information contained within the file is a good checksum when comparing package files actually on your machine and there relative paths to the project (all have to be correct for ILMerge to work). Also the target framework has to be set correctly.

Signing Assemblies for CRM

The Plugin DLL needs to be signed with or without a password. In addition your custom library will need to be signed for this to work as expected in CRM. In this example I created two separate key files with the same name “signingkey.snk” one in the early binding class project and one for the actual final plugin assembly. However surprisingly the 3rd party library newtonsoft (JSON.Net) was successfully merged I assume it may have been signed by author or this could be a red herring when it comes to 3rd party DLL requirements.

System.IO.FileNotFoundException : Unable to find ILMerge Executable

This is the error I was receiving when trying to build my plugin project that was referencing the early bound Common CRM class DLL. I discovered that probably due to “re-distribution” legal reasons that although the packages had been downloaded the target folder “packages\ilmerge.2.14.1208” did indeed not contain ILMerge.exe. Instead go into the packages\ilmerge.2.14.1208\tools sub-directory and copy it from there into the folder “ilmerge.2.14.1208”.

The build then worked successfully.

Specifying Assemblies for Merge

Within a Visual Studio solution you can have potentially many projects each with their own references. The question then arises how do I specify what assemblies to merge? It is important you have set the order in which projects are built within Visual Studio using the “Project Build Order” menu option when you right click on the solution/project. Next you need to pay special attention to the “properties” window of each of the assemblies you have referenced within the project. The property “Copy Local” should be set to “True” for all the assemblies you wish to get merged. As an example: Lets say you have an early bound project called Common, you would within your Plugin/Workflow project have created a reference to it by browsing to the DLL debug folder location, after you have done this you would specify within the plugin project that the Common assembly should Copy Local. This informs the ILMerge during build to Merge it.

That is not the end of the story, even if you have done the above the Common library will not be merged unless you have also in addition instantiated an instance of a class from the Common library within your Plugin code (in other words you have code that use the classes and methods defined in the referenced Common assembly).

If using the standard developer toolkit you normally end up with two output assemblies yourplugin.dll and yourworkflow.dll and those each contain merged DLL’s such as Common.dll for early binding and any other referenced DLLs that you may have merged.

Remember adding the code direct to the DLL is the preferred and recommended approach to avoid CRM Plugin errors so always go back to basics if things don’t work as expected it may be down to the merging process.

All assemblies with Copy to Local set to true will be merged. Conversely, you normally ensure that all CRM SDK and Microsoft Core assemblies have the set “Local Copy” to false.

How to check everything was merged

Within a Visual Studio you will notice that when you build your solution that the Output window contains the steps MSBuild has taken to merge the relevant assemblies. There will be a line which says “Merged Assemblies: yourdll.; next DLL; next DLL; etc”. You can also use an assembly de-compiler tool such as the excellent  Jet Brains Dot Peek. It is a very easy to use tool which basically allows you to see what classes are part of an assembly and see the source code within the assemblies. Therefore in our earlier examples, within the Plugin.DLL file we’d expect to see the early bound classes within it and any other classes from 3rd party assemblies we may have merged such as newtonsoft.dll.

Testing it all works

The first time you go through this it will be painful but at least you have this guide to help you which is more than what I had at the time. My advice is to do each step slowly, go the extra mile by using something like Dot Peek to interrogate the contents of the DLL and ensure it works outside of CRM; before then deploying to CRM and expecting it to all work first time! (That is unlikely) so just be realistic here and work through each stage logically ensuring you understand why each step is required for this to work. Also remember that officially Microsoft no longer support this tool and approach because it can lead to unpredictable behavior as discussed in Part 1.

Best of luck! That completes this two part article on: how to register an early bound library within Microsoft Dynamics CRM Online Sandbox Isolation Mode. Any questions or comments as always feel free to contribute.

Photo Credit

Negative Space free image no copyright restriction

Advertisements

How to register an early bound library within Microsoft Dynamics CRM Online Sandbox Isolation Mode (Part 1)

That’s quite a mouthful, spent at least 3 minutes just trying to figure out an appropriate title for this article. This article does not just relate to early bound assemblies but also any other assembly that is not part of the Microsoft core global assembly cache or the custom Plugin assembly itself.

Let’s say you have a plugin or a custom workflow activity that needs to some fancy stuff and you wish to leverage an existing dynamic link library (DLL) from a 3rd party.  Well you are out of luck if you are using Microsoft Dynamics CRM online (well almost!), drum roll please……..

I’m not going into the pros and cons of late binding vs. early binding that perhaps is a subject for another article however know that Microsoft’s official standing on this is to use Early Bound whenever this is possible to do so: Microsoft Dynamics CRM Best Practices

For now I am going to assume you are like me a sensible soul who values things like improved code readability, portability, reducing the chances of errors and being able to access CRM types properties, methods, and events at Compile time. If that’s the case this article will be very useful to you.

Untitled.png

Oh no not that error again! or perhaps you received something similar to the following error message:

“System.Runtime.Serialization.SerializationException: Microsoft Dynamics CRM has experienced an error. -2147220970 “

What is going on?

Plugins and custom activity workflows that execute within Microsoft Dynamics CRM online always run in Sandbox isolation mode. This is to limit potential security vulnerabilities and reduce the chances of code doing something it really should not be doing on a operating system level.

If using an On Premise version of Dynamics CRM you have two options when registering a plugins isolation mode namely “sandbox” and “none”. If the latter the plugin is able to access other assemblies, resources and code outside of the security context of the CRM application (IIS). In addition other things such as accessing files directly on the file system or perhaps reading and writing to the Windows Registry.

Imagine a scenario, where you want all of the relevant entities and custom entities you have created within a Dynamics CRM solution available to you from multiple project types within Visual Studio, it could be for example:

  • Plugin Project
  • Workflow Project
  • Data Integration Project
  • Early Bound Common Project

Wouldn’t it be nice to be able to easily access the latest, one common early bound library from all those projects? I think so. The problem is the isolation mode, when you register your plugin assembly it will not include the Early Bound DLL, neither will the Custom Workflow activity assembly which means when the CRM application tries to access the early bound types it isn’t going to have a clue about what those things are and where to find them.

Great, so what are our options?

frustrated

Well there are several and this is something I ranted on about in the Dynamics Community website (Error in Sandbox Plugin). Fortunately,  I was able to get support and insights from Scott Durrow (if you don’t know he is the person who developed the excellent Ribbon Workbench Tool) and is well known by Microsoft, IBM and the Dynamics Community.

The general accepted view is “to not generate a separate assembly” but instead link to the project from within Visual Studio this means that the code will be included within each assembly you generate. That sounds OK … but… what if I don’t have the code? what if I have a number of 3rd party libraries that I wish to use rather than re-inventing the  wheel?

It doesn’t meet those scenarios and the truth is I am just way too stubborn to accept something which to me doesn’t instinctively feel right. I like the idea of having all the early bound stuff encapsulated within it’s own library that can be then referenced from any type of application.

Our Answer: ILMerge

It’s name is as Ugly as it is to get up and running in the first place! ILMerge is a utility developed by Microsoft Research that allows multiple DLLs to be merged together into a single DLL Library.

Before we get into this epic drama it is worth mentioning that Microsoft do not officially recommend this approach as it has been shown to sometimes cause issues with Plugin assemblies and types not casting as expected leading to serialisation and plugin execution exceptions at run-time. The general consensus from the community would seem to be:

For Online and this Scenario:

  • Avoid ILMerge if possible and instead look for open source alternatives and include the code in your Plugin and Custom workflow activity projects
  • Add the early bound code class files to each and every CRM project and Solution. To make this easier use Add-Link from within Visual Studio (you reference the external file via a Link)

Of course for On Premise you have more options and can reference other libraries so long as the plugin is set to execute in the “None” isolation mode:

  • Register the assembly on disk with any referenced assemblies in the same directory.
  • Register the referenced assembly in the Global Assembly Cache (GAC)

If you were not to use ILMerge, you can achieve something similar to referencing a DLL (meaning you still have one master common library) doing it via a link instead. Linked project items in Solution Explorer, can be identified by the link indicator in its icon (a small arrow in the lower left corner).

By linking to a file, you can capture ongoing changes to a source file without having to manually update a copy whenever changes are made. However, if the underlying file is deleted, the link will be broken.

  1. In Solution Explorer, select the target plugin/workflow project
  2. On the Project menu, select Add Existing Item
  3. In the Add Existing Item dialog box, locate and select your Common Project early bound class items item you want to link
  4. From the Open button drop-down list, select Add As Link

Should I even bother to use ILMerge?

For simplistic plugins that are just referencing an early bound library and not lots of other third party DLL’s it is unlikely you will have issues with ILMerge. Even so the problem still exists of what to do when using Microsoft Dynamics CRM Online where your plugins/workflows need to leverage third party libraries and the Isolation mode is Sandbox as opposed to None. In that scenario this is not possible unless you Merge the assemblies and to see how to set that up I welcome you to read Part 2

Photo Credits

Howard Sandford

Ken Whytock

All under under Creative Commons License

Powered by WordPress.com.

Up ↑