si2030
si2030

Reputation: 4035

dotnet Core - Entity Framework - Multiple DbContexts choosing wrong DbContext

I have two databases. When first started the solution will check for databases and then create them via a migration. The migrations for both DB's exist however when called to create the context from startup its actually going to the wrong service and I dont know why.

Here is the code that is, among other things creating the context.

public static class StartupCATALOGExtension
{
    public static void EnsureCATALOGMigrationAndInitialise(this IApplicationBuilder app)
    {
        if (app == null) throw new ArgumentNullException(nameof(app));

        using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
        {
            var context = serviceScope.ServiceProvider.GetService<CATALOGContext>();
            var cryptoService = serviceScope.ServiceProvider.GetService<ICryptoService>();

            context.Database.Migrate();
            serviceScope.ServiceProvider.GetService<ICATALOGCounterInitialiser>()
                .InitialiseCATALOGCounters(context);
            serviceScope.ServiceProvider.GetService<ICATALOGStateInitialiser>().CATALOGInitialiseStates(context);
            serviceScope.ServiceProvider.GetService<ICATALOGSuburbInitialiser>().CATALOGInitialiseSuburbs(context);
            serviceScope.ServiceProvider.GetService<IRoleInitialiser>().InitialiseRoles(context);
            serviceScope.ServiceProvider.GetService<ITenantAndUserInitialisations>()
                .InitialiseTenantsAndUsers(context, cryptoService);
        }
    }
}

}

I can step my way down to this line:

var context = serviceScope.ServiceProvider.GetService<CATALOGContext>();

At which point it goes off to the Startup.cs file and in particular the services section. I have this there:

        services
            // Add framework services.
            .AddOptions()

            .AddDbContext<CATALOGContext>(options => options
                .UseSqlServer(Configuration
                    .GetConnectionString("CatalogConnection"), b => b.MigrationsAssembly("JobsLedger.CATALOG")))

            .AddDbContext<DATAContext>(options => options
                .UseSqlServer(Configuration
                    .GetConnectionString("TenantDbConnection"), a => a.MigrationsAssembly("JobsLedger.DATA")))

Its suppose to go specifically to:

            .AddDbContext<CATALOGContext>(options => options
                .UseSqlServer(Configuration
                    .GetConnectionString("CatalogConnection"), b => b.MigrationsAssembly("JobsLedger.CATALOG")))

However for whatever reason that I cannot fathom its going to the other service...

            .AddDbContext<DATAContext>(options => options
                .UseSqlServer(Configuration
                    .GetConnectionString("TenantDbConnection"), a => a.MigrationsAssembly("JobsLedger.DATA")))

I have no idea why it would jump right past the service its suppose to go to and end up with the other database's service..

Why is is going to the second service and more importantly how do you make it go to the correct service?

EXTRA INFO

For completeness here are the connection strings in my appsettings file:

"ConnectionStrings": {
    "CatalogConnection": "Server=(localdb)\\mssqllocaldb;Database=catalogDb;Trusted_Connection=True;MultipleActiveResultSets=true",
    "TenantDbConnection": "Server=(localdb)\\mssqllocaldb;Database=masterDb;Trusted_Connection=True;MultipleActiveResultSets=true"
},

and here is the migrations file:

using JobsLedger.AUTHORISATION.API.SessionMiddleware;
using JobsLedger.CATALOG;
using JobsLedger.DATA;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;
using System.IO;

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

            var builder = new DbContextOptionsBuilder<CATALOGContext>();

            var userSession = new UserSession {
                ConnectionString = configuration.GetConnectionString("CatalogConnection")
            };

            builder.UseSqlServer(userSession.ConnectionString);

            return new CATALOGContext(builder.Options, userSession);
        }
    }
}
public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<DATAContext> {
    public DATAContext CreateDbContext(string[] args) {
        IConfigurationRoot configuration = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json")
            .Build();

        var builder = new DbContextOptionsBuilder<DATAContext>();

        var userSession = new UserSession {
            ConnectionString = configuration.GetConnectionString("TenantDbConnection")
        };
        return new DATAContext(builder.Options, userSession);
    }
}

Upvotes: 0

Views: 547

Answers (1)

si2030
si2030

Reputation: 4035

Well with the overwhelming interest in this question I did a bit of research tonight.

I loaded the actual error - at least the last part and I found THIS post response by Rowan Miller.

Essentially I needed to use a generic options type. So, I changed my constructor in each DbContext to reflect what it actually is..

For my CATALOGContext I changed from this:

public CATALOGContext(DbContextOptions options, IUserSession userSession) : base(options)
        {
            _userSession = userSession;
        }

To this:

public CATALOGContext(DbContextOptions<CATALOGContext> options, IUserSession userSession) : base(options)
        {
            _userSession = userSession;
        }

..and now it chooses the right context to use.

Upvotes: 1

Related Questions