My project depends on two different EntityFramework data layers with different versions of Entity Framework
…Get me out of here! So how can we make a .Net solution work with more than one version of Entity Framework in the mix? It’s a horrible problem; here are my comprehensive field notes.
Imagine the following projects:
- Data5 - A compiled DLL reference; a legacy data layer with Entity Framework 5
- Data6 - A new data layer project with Entity Framework 6
- Consumer - A web project which employs both Data5 and Data6 as dependencies
Fix the Consumer web.config (or app.config)
One config section per EF version
You can define a section to configure each version of EF by specifying the Version
in the type
attribute, and you can give them any name you want (as long as they’re different):
<configSections>
<section name="entityFrameworkV5" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
<section name="entityFrameworkV6" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections>
Then, later in your config file, you can fill these in. They can even be the same in content! It’s all about EF casting your EntityFrameworkSection
to its particular version of that class:
<entityFrameworkV5>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
<providers>
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
</entityFrameworkV5>
<entityFrameworkV6>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework" />
<providers>
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
</entityFrameworkV6>
One connection string per EF version
You may need two different connection strings or you’ll get an error like:
Schema specified is not valid. Errors:
The relationship ‘…’ was not loaded because the type ‘App.Thing’ is not available.
The following information may be useful in resolving the previous error:
The required property ‘ThingID’ does not exist on the type ‘App.Thing’.
I don’t fully know what’s going on with this but I fixed it:
<add name="v6Entities" connectionString="Data Source=localhost;Initial Catalog=MyData;Integrated Security=True;MultipleActiveResultSets=True;Async=True;App=MyApp" providerName="System.Data.SqlClient" />
<add name="v5Entities" connectionString="metadata=res://*/MyData.csdl|res://*/MyData.ssdl|res://*/MyData.msl;provider=System.Data.SqlClient;provider connection string="Data Source=localhost;Initial Catalog=MyData;User Id=AppSecurity;Password=5uPrSSh2b£tPw;MultipleActiveResultSets=True;App=MyApp"" providerName="System.Data.EntityClient"/>
Make sure that your connection string names match the names referenced in the data layer projects. In my case, for example, instead of v5Entities
I picked the name of the connection string used by the legacy data layer project.
Likewise, the Context
class for your new data layer should have a constructor like public MyContext() : base("name=v6Entities")
Magic with dependentAssembly
You can use this combo of bindingRedirect
and codeBase
directives to make the calling code choose the right EF version in each case. It may be possible to make this simpler, but it works for me:
<dependentAssembly>
<assemblyIdentity name="EntityFramework" publicKeyToken="b77a5c561934e089" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
<codeBase version="5.0.0.0" href="..\..\Lib\EnitityFramework5.0\EntityFramework.dll" />
<bindingRedirect oldVersion="5.0.0.1-6.0.0.0" newVersion="6.0.0.0" />
<codeBase version="6.0.0.0" href="..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll" />
</dependentAssembly>
Fix the Consumer .csproj:
The DLLs you include as references to the csproj don’t matter as much as what we did in the web.config with the dependentAssemblies.
But you might still need to make tweaks here, particularly if you see an error like this:
Schema specified is not valid. Errors:
MyContext.ssdl(2,2) : error 0152: No Entity Framework provider found for the ADO.NET provider with invariant name ‘System.Data.SqlClient’. Make sure the provider is registered in the ‘entityFramework’ section of the application config file. See https://go.microsoft.com/fwlink/?LinkId=260882 for more information.
Option 1 is to add a reference to the DLL for the provider you are using (usually EntityFramework.SqlServer.dll
). I’ll assume you already have a project in the solution with the relevant NuGet package… your data layer should tick that box. The reference should like this:
<Reference Include="EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
<HintPath>..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll</HintPath>
</Reference>
Option 2 is to add a sneaky bit of code to make an explicit dependency on the library, to force MSBuild into grabbing it. You can simplify the dependency cheat to the following, which you would put in the Context
constructor:
var type = typeof(System.Data.Entity.SqlServer.SqlProviderServices);
if(type == null) { throw new Exception("Do not remove, ensures static reference to System.Data.Entity.SqlServer"); }
I am not sure whether you need any reference to EntityFramework
in your Consumer project, or which version you need, but I have a reference to v5:
<Reference Include="EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\Lib\EnitityFramework5.0\EntityFramework.dll</HintPath>
</Reference>
That’s all I’ve got. I hope you fix your solution!