Sunday 3 May 2015

How to add external dll reference in crm dynamics using code.

How to add external dll reference in CRM dynamics using code.


You may came across with a situation in which you need some external Dll references in dynamics CRM. I was working on some Integration related customization and found out that I have to parse some files in JASON format from external system, but when I tried to register my Dll on Plugin registration tool, I faced a DLL missing error in at that time. I was registering my DLL on Database.

After some RAD, I found out that the best possible solution to embed the external Dll's to dynamics CRM is to merge them. I found many merging solution on internet but in order to avoid any risk I use the following steps by which i easily able to merge all the dll and successfully register and run them.

This method has only been test on CRM 2015 plugins, but should work for many other scenarios. There are some pros and cons however.

Pros:

  • Everything is contained in 1 DLL.
  • Easier to deploy up to a server (no uploading dependencies separately) , or give to someone to use.

Cons:

  • The DLL can bloat to large sizes (with CRM you’ll have to increase the upload size so you can upload your plugin).
  • If one of the DLL’s within your DLL get’s updated you have to recompile your whole DLL with the new DLL.
  • If you’re using Xrm.DLL in your plugin you’ll need to recompile your A LOT (because of the new fields being added to CRM)
  • We’ve used this method quite a bit now, and it works perfectly for us. We don’t use a lot of 3rd party vendors so we don’t have to recompile often.

Here we go:
I’m going to assume you’ve created a C# class library at this point. We need to edit our .csproj file.
Right click your project –> Click Unload Project (it will go grey and say unavailable). Right click your project again –> Click .csproj

Scroll down to the bottom and find this line:


<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.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.  <Target Name="BeforeBuild">  </Target>  <Target Name="AfterBuild">  </Target>
  -->
 Now paste this code underneath the comments (I like to keep the comments intact):


<Target Name="AfterResolveReferences">  <ItemGroup>    <EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">      <LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName>    </EmbeddedResource>  </ItemGroup></Target>
It should look something like this now: 

<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.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.<Target Name="BeforeBuild"></Target><Target Name="AfterBuild"></Target>--><Target Name="AfterResolveReferences">  <ItemGroup>    <EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">      <LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName>    </EmbeddedResource>  </ItemGroup></Target>
Now, you need to set ONLY the references that you want to copy into the DLL as “Copy Local” = true. 

Now, we can reload our project by Right clicking our project –> Reload Project. Open up your plugin main class and add this event handler:

private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
{

  Assembly executingAssembly = Assembly.GetExecutingAssembly();
  AssemblyName assemblyName = new AssemblyName(args.Name);
  string path = assemblyName.Name + ".dll";

  if (assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) == false)
  {
     path = String.Format(@"{0}\{1}", assemblyName.CultureInfo, path);
  }

  using (Stream stream = executingAssembly.GetManifestResourceStream(path))
  {
       if (stream == null)
           return null;
       byte[] assemblyRawBytes = new byte[stream.Length];
       stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
       return Assembly.Load(assemblyRawBytes);
  }

}

Create a static constructor, we need to hook up the event handler. Add this code:

static TestEntityPlugin()
{
  AppDomain.CurrentDomain.AssemblyResolve  = OnResolveAssembly;

}
You should now be able to compile your DLL! You won’t notice any differences in Visual Studio, but if you use .NET reflector to view the .DLL you’ll notice that the references (with “Copy Local” = true) are within the DLL now.
Basically what’s happening is when the DLL is used, it looks within itself to access for the reference instead of looking elsewhere.
Caveats: The plugin must be registered as “Non-sandbox” in order to have the permissions to use the AssemblyResolve event handler.

The user registering the plugin needs to be a deployment manager (otherwise users could inject bad stuff with AssemblyResolve, whether on purpose or by accident).


Have a happy coding :)

Haseeb Jamshed
CRM Dynamics Consultant

1 comment:

  1. Hello Sir,

    Thank you for very useful post for me. I am in same problem like using an external library into my plugIn project.I am following this post for merging external dll into plug in project. in .csproj , I am not seeing section like

    %(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)

    I am using visual studio 2017 professional, and here the section is










    So, how to add this section into my cs project









    ReplyDelete