Microsoft.Xrm.Client Woes and How to Resolve Them
I like to use my blog posts to share insights and lessons learned during InRule client projects. The following scenario is another example of something that I keep seeing come up in my work, as well for a lot of customers.
The scenario is this: when using software that consumes the Dynamics SDK v8+, and when the application is targeting .NET 4.6+ (the Dynamics SDK 8.2.1 requires .NET 4.5.2 or higher), everything will seem to compile just fine, but at runtime an exception is thrown along the lines of this:
System.IO.FileLoadException : Could not load file or assembly ‘Microsoft.Xrm.Sdk, Version=18.104.22.168, Culture=neutral, PublicKeyToken=31bf3856ad364e35’ or one of its dependencies. The located assembly’s manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
At first, I didn’t really pay much mind to the underlying reasons why this kept coming up, but as the saying goes “the squeaky wheel gets the grease.” I thought I’d present my findings here in the hope that it will help others from having to spend their time resolving this issue.
The key nugget of info is buried in the readme file of the Dynamics 365 SDK (download). At the time of this writing (March 2017), the readme states that the Developer Toolkit is not currently available (emphasis mine):
Developer Toolkit not available
An updated Developer Toolkit for Microsoft Dynamics 365 is planned for this release but it is not ready at this time. Please refer to the CRM 2015 SDK for information about the Developer Toolkit for Microsoft Dynamics CRM 2015 and Microsoft Dynamics CRM Online. The CRM 2015 SDK is available for download here: Microsoft Dynamics CRM Software Development Kit (SDK) for CRM Online and CRM 2015 (on-premises)
It might not be immediately apparent to folks (it wasn’t for me) that this Developer Toolkit is really just two assemblies: Microsoft.Xrm.Client.dll, and Microsoft.Xrm.Portal.dll. The Client assembly contains a bunch of very useful extension methods and the like that help make developers more productive. Methods like SetAttributeValue<T> help make integration code much more readable by wrapping up and hiding away routine and tedious things like checking to see if the Attribute exists before accessing it.
I’ll bet that some folks have already put the pieces together and have figured out what the problem is. For those who haven’t, the problem is that the CRM 2015 SDK is built targeting .NET 4.5, whereas the newer versions of the SDK (and presumably the integrating application) targets .NET 4.6.1+.
At runtime, these components attempt to locate the Microsoft.Xrm.Client.dll assembly, and the assembly loader does indeed locate it, but when the loader examines the assembly’s manifest it sees that the Client.dll assembly was compiled against a different, older version of the .NET framework than the domain of the current app. In the absence of any other factors, the loader rejects the located assembly and we get the “Could not load file…” exception. For more information on the .NET assembly loading process, see here.
So is the solution to this problem simply to forego using newer versions of .NET with applications making use of these CRM SDK packages? No, there’s a better much less intrusive way to do this and it’s with binding redirects.
When installing or removing NuGet pacakges, the default behavior is to add/remove assembly binding redirects, but sometimes this process fails (usually due to file being inaccessible or locked for modification during the operation). To ensure that your application’s runtime is able to correctly locate and load the Xrm.Client assembly, add this redirect to your app.config or web.config:
<assemblyIdentity name="Microsoft.Xrm.Sdk" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-22.214.171.124" newVersion="126.96.36.199" />
It seems somewhat counter-intuitive that you’d add a binding redirect for an assembly that isn’t exhibiting any problems, but if you think about it, the entry makes sense:
The Microsoft.Xrm.Client.dll is looking for (because it depends upon) Microsoft.Xrm.Sdk.dll. Because the compiler doesn’t possess any time travel capabilities (that we know of…) it’s unable to account for future versions of dependencies, even if they are fully compatible. Adding the redirect tells the Xrm.Client assembly, “It’s cool – go ahead and use this version of Microsoft.Xrm.Sdk even though you’ve never heard of it.”. See here for more on binding redirects and .NET.