Reputation: 1698
I'm running into an issue when trying to access some spatial data from a Sql Server table.
The exception I receive is
Unable to load DLL 'SqlServerSpatial.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E)
I tried the solutions mentioned here, and here, which is in essence
Microsoft.SqlServer.Types
package.Microsoft.SqlServer.Types
, EntityFramework
and EntityFramework.SqlServer
references are set to Copy always
SqlServerTypes.Utilities.LoadNativeAssemblies(HttpRuntime.BinDirectory);
to the end of the Application_Start
method in Global.asax.cs
After doing these, I end up with the same exception occurring in the same place, which in a way makes sense, because I don't have a SqlServerSpatial.dll
, but rather a SqlServerSpatial140.dll
included with the Microsoft.SqlServer.Types
package.
What can I do to resolve this issue?
Upvotes: 4
Views: 2457
Reputation: 3950
I came across this same problem recently and decided to dig into it because all of the answers I found on Stack Overflow were voodoo and hand-waving to fix the symptom instead of understand the problem.
Note: This applies to Entity Framework 6 and .NET Framework. Since newer versions of .NET don't have a GAC this may not be a problem there.
What I found is that when using EntityFramework.SqlServer
alongside Microsoft.SqlServer.Types
can lead to runtime assembly version problems depending on what versions of the Types
assembly you have installed in the GAC or other locations in the assembly resolution path. This is pretty easy to happen, since this assembly comes with SQL Server, SQL Server Management Studio, as a standalone installer (which, ironically, some answers suggest using to fix this problem), and probably other ways.
When using SQL Server CLR types like DbGeography
, Entity Framework delegates the work out to the Microsoft.SqlServer.Types
library (even though there isn't a dependency between the packages). To do this it has to load that assembly, and to decide which assembly version to load, it uses the System.Data.Entity.SqlServer.SqlTypesAssemblyLoader
class that has the following heuristic:
System.Data.Entity.SqlServer.SqlProviderServices.SqlServerTypesAssemblyName
is populated, use that.The current major version of Microsoft.SqlServer.Types
is 14. This means if you have an older version of 11 or 10 installed in the GAC, those versions will be preferred by Entity Framework and will be loaded into your AppDomain, even if you already have version 14 loaded from the nuget package.
This leads to multiple versions of the Microsoft.SqlServer.Types
assembly being loaded with the older version of the library getting used in ways that are not supported, resulting in runtime exceptions. Symptoms include looking for the old SqlServerSpatial.dll
native library instead of the expected SqlServerSpatial140.dll
native library which comes with version 14 of the Microsoft.SqlServer.Types
nuget package (and is loaded via the Loader.cs file that comes with that package).
You can verify this is happening by enabling Fusion binding logs, or by adding the following to your program (before and after using Entity Framework) and noting that you go from one assembly to two with different versions.
AppDomain.CurrentDomain.GetAssemblies()
.Where(a => a.FullName.Contains("Microsoft.SqlServer.Types")).ToArray();
The end result is an exception being raised, such as Exception has been thrown by the target of an invocation. --> Unable to load DLL 'SqlServerSpatial.dll'
.
There are two separate ways to address the problem, depending on your situation and preference. You need to do one of these (I suggest leaving a comment for the poor future developer who runs into this again after Nuget packages are updated):
Add an assembly redirect binding for Microsoft.SqlServer.Types
so that Entity Framework (and everything else) is forced to use the version you have via Nuget:
<dependentAssembly>
<assemblyIdentity name="Microsoft.SqlServer.Types" publicKeyToken="89845dcd8080cc91" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-14.0.0.0" newVersion="14.0.0.0" />
</dependentAssembly>
Specify the version you want Entity Framework to use via the static property SqlProviderServices.SqlServerTypesAssemblyName
property. You'll need to do this early in the lifecycle of the program, such as in Startup
/ Main
/ Application_Start
/ etc.
System.Data.Entity.SqlServer.SqlProviderServices.SqlServerTypesAssemblyName
= "Microsoft.SqlServer.Types, Version=14.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91";
That's it.
Upvotes: 2
Reputation: 1
Make a copy of your SqlServerSpatial140.dll
(or download a newer version) and name your copy SqlServerSpatial.dll
and move it to your system32
directory. Worked for me.
Upvotes: 0