Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EF6 'DbConfigurationClass' was set but this type was not discovered - multiple DbContexts and DbConfigurations

I have a solution in which we have two DbContexts, and we are in the process of moving from EF4 to EF6. The older DbContext was a code-first and we are mostly using the newer generated db-first, but need both in operation due to external dependencies.

My classes look like this:

namespace Old.Busted.EF.DAL
{
    [DbConfigurationType(typeof(Old.Busted.EF.DAL.OldConfiguration))]
    public class OldContext : DbContext[...]

    public class OldConfiguration : DbConfiguration[...]
}

namespace New.Shiny.EF.DAL
{
    [DbConfigurationType(typeof(New.Shiny.EF.DAL.NewConfiguration))]
    public class NewContext : DbContext[...]

    public class NewConfiguration : DbConfiguration[...]
}

The precise error that I am getting is

An instance of 'NewConfiguration' was set but this type was not discovered in the same assembly as the 'OldContext' context. Either put the DbConfiguration type in the same assembly as the DbContext type, use DbConfigurationTypeAttribute on the DbContext type to specify the DbConfiguration type, or set the DbConfiguration type in the config file.

which is trying to apply the new configuration to the old context. The library in which the breaking code is sitting is the only library which references both the old and new EF DALs, and moreover this exception only gets thrown when the tests are being run on the command line via mstest - they pass just fine running from within Visual Studio.

Using .NET 4.0 and Visual Studio 2010.

Things I have tried:

  • putting config info in config files instead of code (no change)
  • putting a single DbConfiguration into a shared library (broke even more things)
  • using a DbContext constructor passing in a DbConnection object instead of the parameterless or string constructor (no change)
like image 535
Zind Avatar asked Dec 06 '13 18:12

Zind


3 Answers

Easiest solution seems to have been to move to config-file based configuration, as detailed here.

The reason I couldn't get this to work the first time is because I had a different version of EF listed in one of the various config files and didn't catch it.

I did try using a single DbConfiguration class in a common library and was able to get it to work this time (with no real fiddling, I must have just done something terribly wrong the first time) but I think that the config-file based configuration is the better solution.

Putting configuration information in a config file, how novel!

like image 195
Zind Avatar answered Oct 12 '22 03:10

Zind


According to Microsoft you can solve two DbContexts with DbConfiguration like this:

XML:

<entityFramework codeConfigurationType="MyNamespace.MyDbConfiguration, MyAssembly">
    ...Your EF config...
</entityFramework>

Code:

[DbConfigurationType(typeof(MyDbConfiguration))]
public class MyContextContext : DbContext
{
}

[DbConfigurationType("MyNamespace.MyDbConfiguration, MyAssembly")]
public class MyContextContext : DbContext
{
}

https://docs.microsoft.com/en-us/ef/ef6/fundamentals/configuring/code-based

I did not solve multiple DbConfiguration however. My solution was sharing the same DbConfiguration for both DbContexts like this:

public class DbContextConfiguration : DbConfiguration
{
    public DbContextConfiguration()
    {
        var providerInstance = SqlProviderServices.Instance;
        SqlProviderServices.TruncateDecimalsToScale = false;
        this.SetProviderServices(SqlProviderServices.ProviderInvariantName, SqlProviderServices.Instance);
    }
}

[DbConfigurationType(typeof(DbContextConfiguration))]
public class DbContext1 : DbContext
{

}

[DbConfigurationType(typeof(DbContextConfiguration))]
public class DbContext2 : DbContext
{

}
like image 23
Ogglas Avatar answered Oct 12 '22 01:10

Ogglas


Multiple DbContext classes WITHOUT configuration file (except for connectionStrings):

You only need to add [DbConfigurationType(typeof(CodeFirstDbConfiguration))] to every class that inherits the DbContext class.

It uses Reflection. I don't mind that :)

Please check it out!


Depending on the C# or the .NET versions, you may need to a few some lines of code (e.g., from is not null to != null)


App.config:

No need to specify the entityFramework configSection!

No need to specify the entityFramework providers!

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <clear/>
    <add name="Foo_MySqlDbContext"
         connectionString="Server=yyy ; Database=yyy ; Uid=yyy ; Pwd=yyy ; Connect Timeout=30 ; SslMode=none ;"
         providerName="MySql.Data.MySqlClient" />
    <add name="Foo_NpgsqlDbContext"
         connectionString="Server=yyy ; Port=yyy ; Database=yyy ; UserId=yyy ; Password=yyy ;"
         providerName="Npgsql" />
  </connectionStrings>
</configuration>

ConsoleApp1.csproj:

Does NOT need to target NET 6.0

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="EntityFramework" Version="6.4.4" />
    <PackageReference Include="EntityFramework6.Npgsql" Version="6.4.3" />
    <PackageReference Include="MySql.Data" Version="8.0.18" />
    <PackageReference Include="MySql.Data.EntityFramework" Version="8.0.18" />
    <PackageReference Include="Npgsql" Version="5.0.10" />
  </ItemGroup>

</Project>

Program.cs:

using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Core.Common;
using System.Reflection;

namespace ConsoleApp1
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("Foo_NpgsqlDbContext");
            try
            {
                using (var db = new Foo_NpgsqlDbContext())
                    Console.WriteLine("Exists : " + db.Database.Exists());
            }
            catch (Exception exception)
            {
                Console.WriteLine(exception.Message);
            }

            Console.WriteLine();

            Console.WriteLine("Foo_MySqlDbContext");
            try
            {
                using (var db = new Foo_MySqlDbContext())
                    Console.WriteLine("Exists : " + db.Database.Exists());
            }
            catch (Exception exception)
            {
                Console.WriteLine(exception.Message);
            }

            Console.ReadLine();
        }
    }

    [DbConfigurationType(typeof(CodeFirstDbConfiguration))]
    public class Foo_NpgsqlDbContext : DbContext
    {
        public Foo_NpgsqlDbContext()
            : base("name=Foo_NpgsqlDbContext") { }
    }

    [DbConfigurationType(typeof(CodeFirstDbConfiguration))]
    public class Foo_MySqlDbContext : DbContext
    {
        public Foo_MySqlDbContext()
            : base("name=Foo_MySqlDbContext") { }
    }

    /// <summary>
    /// usage:
    /// [DbConfigurationType(typeof(CodeFirstDbConfiguration))]
    /// </summary>
    public class CodeFirstDbConfiguration
        : System.Data.Entity.DbConfiguration
    {
        private static bool _initialized = false;

        public CodeFirstDbConfiguration()
        {
            if (_initialized) return;
            _initialized = true;

            //return; // Uncomment to make the application NOT work

            ConfigureMySql();
            ConfigureOracle();
            ConfigurePostgreSql();
            ConfigureSqlServer();
            //Configure...(); // configure other database kinds
        }

        #region MySql
        private void ConfigureMySql()
        {
            var services = Type.GetType("MySql.Data.MySqlClient.MySqlProviderServices, MySql.Data.EntityFramework")
                ?.GetConstructor(Array.Empty<Type>())
                ?.Invoke(null) as DbProviderServices;
            if (services is not null)
                SetProviderServices("MySql.Data.MySqlClient", services);

            if (DbProviderFactories.GetProviderInvariantNames().Contains("MySql.Data.MySqlClient") == false)
                DbProviderFactories.RegisterFactory(
                    "MySql.Data.MySqlClient",
                    "MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.0.18.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d"
                );
        }
        #endregion

        #region Oracle
        private void ConfigureOracle()
        {
            var services = Type.GetType("Oracle.ManagedDataAccess.EntityFramework.EFOracleProviderServices, Oracle.ManagedDataAccess.EntityFramework")
                ?.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, Array.Empty<Type>())
                ?.Invoke(null) as DbProviderServices;
            if (services is not null)
                SetProviderServices("Oracle.ManagedDataAccess.Client", services);

            if (DbProviderFactories.GetProviderInvariantNames().Contains("Oracle.ManagedDataAccess.Client") == false)
                DbProviderFactories.RegisterFactory(
                    "Oracle.ManagedDataAccess.Client",
                    "Oracle.ManagedDataAccess.Client.OracleClientFactory, Oracle.ManagedDataAccess, Version=4.122.1.0, Culture=neutral, PublicKeyToken=89b483f429c47342"
                );
        }
        #endregion

        #region PostgreSql
        private void ConfigurePostgreSql()
        {
            var services = Type.GetType("Npgsql.NpgsqlServices, EntityFramework6.Npgsql")
                ?.GetConstructor(Array.Empty<Type>())
                ?.Invoke(null) as DbProviderServices;
            if (services is not null)
                SetProviderServices("Npgsql", services);

            var factory = Type.GetType("Npgsql.NpgsqlFactory, Npgsql")
                ?.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, Array.Empty<Type>())
                ?.Invoke(null) as DbProviderFactory;
            if (factory is not null)
                SetProviderFactory("Npgsql", factory);

            if (DbProviderFactories.GetProviderInvariantNames().Contains("Npgsql") == false)
                DbProviderFactories.RegisterFactory(
                    "Npgsql",
                    "Npgsql.NpgsqlFactory, Npgsql, Culture=neutral, PublicKeyToken=5d8b90d52f46fda7"
                );
        }
        #endregion

        #region SqlServer
        private void ConfigureSqlServer()
        {
            var services = Type.GetType("System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer")
                ?.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, Array.Empty<Type>())
                ?.Invoke(null) as DbProviderServices;
            if (services is not null)
                SetProviderServices("System.Data.SqlClient", services);
        }
        #endregion
    }
}
like image 21
Lucas Avatar answered Oct 12 '22 03:10

Lucas