Adrian Wright
Adrian Wright

Reputation: 83

Can two different versions of EF Core be used by two separate projects that then reference each other?

I have a .NET solution which consists of some .NET Standard 2.0 class libraries, a few .NET Core 5.0 console apps, a .NET Framework 4.8 MVC website and .NET Framework 4.8 Web API.

We use EF Core 3.1.x within the solution to create our database migrations which are applied to a MySql database. We also use NuGet package Pomelo.EntityFrameworkCore.MySql 3.2.7

We have started to upgrade the .NET Framework 4.8 Web API to .NET 6 and as such have taken a dependency on OpenIddict 4.0.0 to handle authorization token generation.

OpenIddict 4.0.0 has a dependency on Microsoft.EntityFrameworkCore.Relational >= 6.0.12 which means in the upgraded .NET 6 API we are using EF Core 6.0.12. N.B. We create a separate database to hold the OpenIddict tables in.

We therefore have 2 DbContext derived classes MyProjectContext and OAuthDbContext.

MyProjectContext lives in .NET Standard 2.0 class library MyProject.Data which references EF Core 3.1.x.

OAuthDbContext lives in the upgraded .NET 6 API MyApi which directly references EF Core 6.0.12 and Pomelo.EntityFrameworkCore.MySql 6.0.12.

MyApi has a project reference to MyProject.Data as we need access to the underlying data that the API returns.

As part of the Program.cs class in the upgraded .NET 6 API we are using Autofac dependency injection to register existing Modules.

One of these Modules is the BaseModule which looks like this:

/// <summary>
/// Autofac base module.
/// </summary>
public class BaseModule : Module
{
    protected override void Load(ContainerBuilder containerBuilder)
    {
        // Register data layer.
        // Database context.
        containerBuilder.Register<DbContext>(c => new MyProjectContext()).InstancePerLifetimeScope();

        // Register services.
        containerBuilder.RegisterType<AccountService>().As<IAccountService>().InstancePerLifetimeScope();
        //etc...
    }
}

The problem I am facing is, when the upgraded .NET 6 API MyApi is run, I get a runtime error when the MyProjectContext default constructor is called from loading the BaseModule, saying it cannot load Pomelo.EntityFrameworkCore.MySql version=6.0.2.0 (which is the version used in the upgraded .NET 6 API MyApi), but the in .NET Standard 2.0 class library MyProject.Data where the error is thrown uses Pomelo.EntityFrameworkCore.MySql version=3.2.7.0.

System.TypeLoadException
  HResult=0x80131522
  Message=Could not load type 'Microsoft.EntityFrameworkCore.MySqlDbContextOptionsExtensions' from assembly 'Pomelo.EntityFrameworkCore.MySql, Version=6.0.2.0, Culture=neutral, PublicKeyToken=2cc498582444921b'.
  Source=MyProject.Data
  StackTrace:
   at MyProject.Data.MyProjectContext.GetMySQLContextOptions(String connectionString) in C:\Users\Adrian\source\repos\myproject\myproject\MyProject.Data\MyProjectObjectContext.cs:line 60
   at MyProject.Data.MyProjectContext..ctor() in C:\Users\username\source\repos\myproject\myproject\MyProject.Data\MyProjectObjectContext.cs:line 18
   at MyProject.Framework.DIModules.BaseModule.<>c.<Load>b__0_0(IComponentContext c) in C:\Users\username\source\repos\myproject\myproject\MyProject.Framework\DIModules\BaseModule.cs:line 53
   at Autofac.RegistrationExtensions.<>c__DisplayClass41_0`1.<Register>b__0(IComponentContext c, IEnumerable`1 p)
   at Autofac.Builder.RegistrationBuilder.<>c__DisplayClass0_0`1.<ForDelegate>b__0(IComponentContext c, IEnumerable`1 p)
   at Autofac.Core.Activators.Delegate.DelegateActivator.ActivateInstance(IComponentContext context, IEnumerable`1 parameters)
   at Autofac.Core.Activators.Delegate.DelegateActivator.<ConfigurePipeline>b__2_0(ResolveRequestContext ctxt, Action`1 next)
   at Autofac.Core.Resolving.Middleware.DelegateMiddleware.Execute(ResolveRequestContext context, Action`1 next)
   at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
   at Autofac.Core.Resolving.Middleware.DisposalTrackingMiddleware.Execute(ResolveRequestContext context, Action`1 next)
   at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
   at Autofac.Core.Resolving.Middleware.ActivatorErrorHandlingMiddleware.Execute(ResolveRequestContext context, Action`1 next)

Is it possible to use two different versions of EF Core in referenced projects?

Upvotes: 0

Views: 520

Answers (1)

lauxjpn
lauxjpn

Reputation: 5254

The issue here is that OpenIddict assumes you want to use EF Core 6 if you target net6.0, even though it is perfectly fine to use EF Core 3.1 with net6.0.


One way to solve this should be to just build the OpenIddict 4.0.0 repository locally.

You should then be able to reference the built netcoreapp3.1 assemblies from your MyApi project.

Your MyApi project would then target net6.0, reference the locally built netcoreapp3.1 assemblies of OpenIddict 4.0.0 and use EF Core 3.1 (via package reference).

Your MyProject.Data project would target netstandard2.0 and use EF Core 3.1 (via package reference).

(Instead of using the netcoreapp3.1 assemblies of OpenIddict, you could of course also just change the code of OpenIddict to make it reference EF Core 3.1 when targeting net6.0, in which case you MyApi project would just reference the net6.0 assemblies of OpenIddict).


Another way is to use multi-targeting for your MyProject.Data project.

You would need some preprocessor conditions around your UseMySql() call, because the signature changed between Pomelo 3.1 and 5.0 (a mandatory server version parameter was added).

Your MyApi project would target net6.0, OpenIddict 4.0.0 (via package reference) and use EF Core 6.0 (via package reference).

Your MyProject.Data project would target netstandard2.0 and use EF Core 3.1 (via package reference) and also target net6.0 and use EF Core 6.0 (via package reference).

Upvotes: 1

Related Questions