Home > OS >  Entity Framework DB First two contexts pointing to the same database schema
Entity Framework DB First two contexts pointing to the same database schema

Time:02-26

In my application, for performance reasons I want to replicate the database to a separate reporting-only database, and keep my transactional work on the primary database and handle updates through synchronization on the SQL Server side. Using this, I want to offload the very intensive data-heavy database operations to the reporting-only (read-only) database. The two database instances are identical in terms of schema. We are developing entity framework database-first, scaffolding the database tables into our application.

I initially set this up where the reporting context used only the reporting tables, which are table schemas created to model output from the stored procedures with which all the reports are generated. So they were distinct names from the transactional database context and everything worked great. Now, however, we've found some other data-heavy queries that we want to offload to the reporting database as well, so we need to scaffold all of the same tables from the transactional database context to the reporting database context.

Here are my two scaffolding commands:

Scaffold-DbContext "Server=SERVERNAME;Database=DBNAME;User ID=ID;Password=PW;" Microsoft.EntityFrameworkCore.SqlServer -NoOnConfiguring -OutputDir Model\DB -Context TransContext -UseDatabaseNames -f -t tablenames...

Scaffold-DbContext "Server=SERVERNAME;Database=DBNAME;User ID=ID;Password=PW;" Microsoft.EntityFrameworkCore.SqlServer -NoOnConfiguring -OutputDir Model\DB\Reporting -Context ReportsContext -UseDatabaseNames -f -t tablenames...

So the tables are scaffolded into a subfolder for the ReportsContext to allow the duplicate tables without overwriting the ones created by the first scaffolding command.

When I scaffold the same tables from the two different databases into the two contexts, I get the error "'TableName' is an ambiguous reference between AppName.Model.DB.TableName and AppName.Model.DB.Reporting.TableName" for every table. The context you use properly identifies the class/table you're referencing, but the instantiation of the table class doesn't know which copy of the table it's supposed to be referencing. I would really hate to have to fully-qualify every database model class throughout the application, is there an easier way that I'm missing?

CodePudding user response:

If you take your DB context, make it abstract (not vital, but stops you new'ing it accidentally) and change its constructor signature:

public abstract partial class MyContext: DbContext
{
    public MyContext(DbContextOptions options): base(options) {}

And then make a couple of new do-nothing contexts that extend it and pass the options through to the base constructor:

public class ReportingMyContext: MyContext 
{
    public ReportingMyContext(DbContextOptions<ReportingMyContext> options) : base(options) { }
}

public class MainMyContext: MyContext
{
    public MainMyContext(DbContextOptions<MainMyContext> options) : base(options) { }

}

And register them with different connection strings:

services.AddDbContext<ReportingMyContext>(options =>
    options.UseSqlServer("Data Source=blah blah blah")
);

services.AddDbContext<MainMyContext>(options =>
    options.UseSqlServer("Data Source=blow blow blow")
);

Then you can get the injector to inject the different context depending on what it sees you use

class ReportsController{
  ReportsController(ReportingMyContext x){
    x.SomeTable.Count() //counts reports db
  }

class UsersController{
  UsersController(MainMyContext x){
    x.SomeTable.Count() //counts main db
  }

There are other ways of varying the connection string, but if you're familiar with and happy with the way getting a context via DI works then it can be handy and simple to just change the connected database by varying the Type of the injected context, or injecting both if you have some heavy reporting/querying need but some updates etc to make too. You might eventually start to add some things that were specific to only one context too.. A similar approach could perhaps be taken with interfaces too, though there's a bit more typing involved in specifying which members of the context are members of the interface so that when you say UsersController(IReportingDbContext x) { x.<thing> then <thing> is actually there without a cast

Other methods such as setting the connection string after you get the context are possible with later EFs..

CodePudding user response:

when you dependency inject in your controller or class the solver will bind to the correct data context. Ensure the connection strings are unique for each datacontext.

  • Related