ASP.NET Core: multiple contexts and databases
How to split a model across multiple DbContext instances and databases in ASP.NET Core with EF Core and Identity: configuration, migrations, DI, usage example, and best practices for scaling and organising logs.
Mateusz Kopta
Introduction
The first version of an application usually comes together smoothly: we have the documentation, configure environments, deploy and… the rising user growth chart is satisfying. Until the database starts growing exponentially and slows down the entire platform.
The source of the problem? A single, monolithic database stores both domain data and operational logs. The solution: split responsibilities across multiple contexts and databases while keeping a consistent model in the code.
Goal and assumptions
- Split data into smaller, independent sets - Have a simple way to handle multiple DbContext instances from within the application - Maintain a straightforward migration process in EF Core
Technology stack
ASP.NET Core 2.x, Entity Framework Core (Code First), and Identity.
Sample application model
Let us assume a simple forum. Users create threads containing posts. Additionally, we record user activity as logs.
- Domain data: Users, Threads, Posts - Logs: UserActionLog
Two contexts, two databases
The first context handles domain data and inherits from IdentityDbContext, which allows it to link users with Thread and Post. The second context, based on DbContext, stores only user log entries.
- DomainDbContext: Identity + Threads + Posts - ActionLogDbContext: UserActionLogs
Benefits:
- Clear responsibility boundaries and easier maintenance - Independent lifecycles and data retention strategies - The ability to scale and tune each database separately
Factories and context aggregator
To conveniently obtain the appropriate DbContext, we define a factory interface and implementations for each context. We register them in ASP.NET Core DI. Finally, we create a lightweight aggregator that wraps the factories and returns the requested context by key or type.
- IDbContextFactory: contract for creating DbContext instances - ActionLogDbContextFactory and DomainDbContextFactory: implementations registered in DI - DbContexts: an aggregating object exposing contexts through an indexer
This allows us to retrieve the correct data source anywhere in the application without duplicating configuration.
Registration in Startup
In ConfigureServices, we register both contexts with separate connection strings from appsettings.json, as well as the factories and the DbContexts aggregator. The web host uses the Startup class when launching the application.
- Example connection string names: DomainDataDbConnection and ActionLogDataDbConnection
Migrations for multiple contexts
EF Core supports multiple contexts in migrations using the -Context parameter and separate migration directories. We run two series of Add-Migration and Update-Database commands, one for each context, also specifying the startup project via -Startup.
Example Add-Migration commands:
Add-Migration Initial -Context DomainDbContext -OutputDir Migrations/DomainDbContextMigrations -Startup MultipleContextsApp.Web
Add-Migration Initial -Context ActionLogDbContext -OutputDir Migrations/ActionLogDbContextMigrations -Startup MultipleContextsApp.Web
Then update the databases:
Update-Database -Context DomainDbContext -Startup MultipleContextsApp.Web
Update-Database -Context ActionLogDbContext -Startup MultipleContextsApp.Web
As a result, we get two independent migration histories and a clear structure in the model layer.
Usage in the application
We inject DbContexts (the aggregator) wherever we need data access: in controllers, handlers or—preferably—in the data access layer (DAL). The DAL then exposes an interface to higher layers.
Example use in a controller:
- The GetActions method retrieves entries from ActionLogDbContext using the ActionLogDataDbConnection connection - The GetThreads method retrieves threads from DomainDbContext using the DomainDataDbConnection connection
Best practices
- Separate data with different dynamics and retention requirements (e.g. logs vs business data) - Provide each context with its own migration directory - Monitor connections and limits, scaling databases independently - Hide data access details behind the DAL to maintain modularity
Summary
We have built a web application skeleton using multiple DbContext instances and databases. We now have a clear separation of responsibilities, simpler migrations, and the flexibility to scale. In larger projects, we inject the context aggregator into the DAL, which simplifies maintenance and further development.
Do you have questions or want to assess whether your database can handle traffic growth? Get in touch with us — we will prepare a free analysis.
Do you need technology support?
Let us talk about your project — from discovery to deployment.
Book a consultationWould you like to know more?
Explore other articles or let’s discuss your project
All articles Let’s design your AI application