Unable to create object error in dotnet ef migrations for MySql DbContext, but works for Sqlite DbContext

I have created 2 DbContexts for a new ASP.Net Core app.

https://github.com/jonasarcangel/IdentityMigrationsForMySql/tree/master/IdentityMigrationsForMySql/Server/Data

They are both derived from the same ApplicationDbContext base class, with the only difference being the OnConfiguring override, where UseMySql is used instead of UseSqlite, following what is suggested in this post about multiple providers.

SqliteDbContext.cs has this:

        protected override void OnConfiguring(DbContextOptionsBuilder options)
        {
#if DEBUG_EF
            options.UseSqlite("DataSource=");
#endif
        }

MySqlDbContext.cs has this:

        protected override void OnConfiguring(DbContextOptionsBuilder options)
        {
#if DEBUG_EF
            options.UseMySql("Server=");
#endif
        }

The following command was run:

dotnet ef migrations add InitialCreate --context MySqlDbContext --output-dir Migrations/MySql --configuration DebugEf

Error returned is:

Unable to create an object of type ‘MySqlDbContext’. For the different
patterns supported at design time, see
https://go.microsoft.com/fwlink/?linkid=851728

However, the following command, which uses the context for Sqlite, is successful.

dotnet ef migrations add InitialCreate --context SqliteDbContext --output-dir Migrations/Sqlite --configuration DebugEf

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

This is because in your ConfigureServices method, you’re explicitly configuring a SqliteDbContext:

public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<SqliteDbContext>(options =>
                options.UseSqlServer(
                    Configuration.GetConnectionString("DefaultConnection")));
            ...
        }

This creates migrations successfully if you change your code to AddDbContext<MySqlDbContext>, but then it would break the Sqlite build.

If you continue reading the article you linked, they describe how to fix this problem:

In your application, you should continue to use MyDb. However, for
EF to determine which migration to use, you have to call
services.GetService<T> where T is one of the subclasses (instead of
MyDb).

Therefore, in Startup.ConfigureServices, register the subclasses:

// Configure database 
switch (config.DbProvider.ToLower()) {
case "sqlite": {
    services.AddDbContext<MyDb>(options => { options.UseSqlite(config.DbConnString); });
    services.AddDbContext<SqliteMyDb>(options => { options.UseSqlite(config.DbConnString); });
}
    break;
case "postgres": {
    services.AddDbContext<MyDb>(options => options.UseNpgsql(config.DbConnString));
    services.AddDbContext<NpgsqlMyDb>(options => options.UseNpgsql(config.DbConnString));
}
    break;
default: {
    throw new Exception($"DbProvider not recognized: {config.DbProvider}");
} }

Then, in Startup.Configure, do the migrations:

void ProcessDb<T>()
where T : MyDb {
    using var db = services.GetService<T>();
    db.Database.Migrate();

    // ... perform other startup tasks with db }

switch (config.DbProvider.ToLower()) {
    case "sqlite": {
        ProcessDb<SqliteMyDb>();
        break;
    }
    case "postgres": {
        ProcessDb<NpgsqlMyDb>();
        break;
    }
    default: {
        throw new Exception();
    } }

In summary, for each extra provider, add one subclass, a two-line case
in ConfigureServices, and a case in Configure.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x