• Home >>
  • ASP.NET Core >>

EntityFramework Core – Add an implementation of IDesignTimeDbContextFactory – Multiple DbContext’s

Introduction

We already talked about problem when Entity Framework Core tooling requires you to implement IDesignTimeDbContextFactory<DbContext> – check out this post.

It gets interesting when you have more than one DbContext in your application and you want to add another implementation of IDesignTimeDbContextFactory, without repeating the code and making sure that EF Core tooling picks the right one.

 

Problem

We have more than DbContext  in Database project and we need an implementation of IDesignTimeDbContextFactory<DbContext>.

First DbContext:

public class CodingBlastDbContext : DbContext
{
    public CodingBlastDbContext(DbContextOptions<CodingBlastDbContext> options)
        : base(options)
    {

    }

    public DbSet<Category> Categories { get; set; }
}

Second DbContext:

public class OtherDbContext : DbContext
{
    public OtherDbContext(DbContextOptions<OtherDbContext> options)
        : base(options)
    {

    }

    public DbSet<Tag> Tags { get; set; }
}

If we try to add migrations from Database project:

dotnet ef migrations add InitialMigration -s ../Web/

We get the following error:

More than one DbContext was found. Specify which one to use. Use the ‘-Context’ parameter for PowerShell commands and the ‘–context’ parameter for dotnet commands.

If we specify the context:

dotnet ef migrations add InitialMigration -s ../Web/ -c OtherDbContext

Now we get this message:

Unable to create an object of type ‘OtherDbContext’. Add an implementation of ‘IDesignTimeDbContextFactory’ to the project, or see https://go.microsoft.com/fwlink/?linkid=851728 for additional patterns supported at design time.

 

Solution

Previously, for one DbContext and one implementation of IDesignTimeDbContextFactory<DbContext> we had this code:

public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<CodingBlastDbContext>
{
    public CodingBlastDbContext CreateDbContext(string[] args)
    {
        IConfigurationRoot configuration = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json")
            .Build();

        var builder = new DbContextOptionsBuilder<CodingBlastDbContext>();

        var connectionString = configuration.GetConnectionString("DefaultConnection");

        builder.UseSqlServer(connectionString);

        return new CodingBlastDbContext(builder.Options);
    }
}

One of the things we can do is to make this class and method generic and make two classes that will inherit this class.

One way to do generic implementation of DesignTimeDbContextFactory and CreateDbContext:

public class DesignTimeDbContextFactory<T> : IDesignTimeDbContextFactory<T>
    where T : DbContext
{
    public T CreateDbContext(string[] args)
    {
        IConfigurationRoot configuration = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json")
            .Build();

        var builder = new DbContextOptionsBuilder<T>();

        var connectionString = configuration.GetConnectionString("DefaultConnection");

        builder.UseSqlServer(connectionString);

        var dbContext = (T)Activator.CreateInstance(
            typeof(T),
            builder.Options);

        return dbContext;
    }
}

Now we can have two separate DesignTimeDbContextFactory implementations:

public class CodingBlastDesignTimeDbContextFactory : DesignTimeDbContextFactory<CodingBlastDbContext>
{
}

public class OtherDesignTimeDbContextFactory : DesignTimeDbContextFactory<OtherDbContext>
{
}

Now we can run the following command:

dotnet ef migrations add InitialMigration -s ../Web/ -c OtherDbContext

 

Ibrahim Šuta

Software Consultant interested and specialising in ASP.NET Core, C#, JavaScript, Angular, React.js.