270

My impression to date has been that a DbContext is meant to represent your database, and thus, if your application uses one database, you'd want only one DbContext.

However, some colleagues want to break functional areas out into separate DbContext classes.

I believe this comes from a good place -- a desire to keep the code cleaner -- but it seems volatile. My gut's telling me it's a bad idea, but unfortunately, my gut feeling is not a sufficient condition for a design decision.

So I'm looking for:

A) concrete examples of why this might be a bad idea;

B) assurances that this will all work out just fine.

Massimiliano Kraus
  • 3,638
  • 5
  • 27
  • 47
Josh Schultz
  • 8,000
  • 9
  • 32
  • 39
  • 1
    See my answer : http://stackoverflow.com/questions/8244405/domain-driven-design-entities-across-bounded-contexts/18714942#18714942 – Mohsen Alikhani Sep 10 '13 at 09:18

11 Answers11

211

You can have multiple contexts for single database. It can be useful for example if your database contains multiple database schemas and you want to handle each of them as separate self contained area.

The problem is when you want to use code first to create your database - only single context in your application can do that. The trick for this is usually one additional context containing all your entities which is used only for database creation. Your real application contexts containing only subsets of your entities must have database initializer set to null.

There are other issues you will see when using multiple context types - for example shared entity types and their passing from one context to another, etc. Generally it is possible, it can make your design much cleaner and separate different functional areas but it has its costs in additional complexity.

Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
  • 26
    Using single context per app can be expensive if the application has many entities/tables. Thus depending on the schema, it might also make sense to have multiple contexts. – DarthVader May 13 '13 at 07:04
  • 9
    Since I don't subscribe to pluralsight, I found this awesome article by Julie Lerman ([her comment](http://stackoverflow.com/a/12625918/65716)) written well after this Q/A, but very appropriate: http://msdn.microsoft.com/en-us/magazine/jj883952.aspx – Dave T. Jul 03 '13 at 15:34
  • I suggest, entity framework to support multiple dbcontexts in same database by naming convention. For this reason I have still been writing my own ORM for modular application purpose. Thats hard to beleive it forces single application to use single database. Especially in web farms you have limited number of databases – freewill Nov 29 '13 at 20:08
  • Additionaly, I realized that You can Enable-Migrations only for one context inside the project via PM Console. – Piotr Kwiatek Nov 22 '14 at 20:32
  • 11
    @PiotrKwiatek Not sure if this changed between your comment and now, but `Enable-Migrations -ContextTypeName MyContext -MigrationsDirectory Migrations\MyContextMigrations` works now. – Zack Aug 10 '15 at 15:30
  • As per @choco's answer, if a "secondary" context needs to share tables in the main context, it does not need to declare the shared table. In a DB first diagram, don't include that table. Manually add it's `DbSet` definition into the secondary context. That can be done in a partial class matching what the EF Designer makes – Glen Little Sep 16 '15 at 22:01
  • @GlenLittle what about the case when model first is used and tables are in different schema? http://stackoverflow.com/questions/36293681/join-on-tables-from-different-dbcontext-model-first – Don Box Mar 30 '16 at 09:41
  • @MikeFills I added an answer to your question. We can discuss there if needed. – Glen Little Mar 30 '16 at 20:11
  • Why must the database initializer be set to null? +1. – w0051977 Sep 20 '17 at 17:52
  • @w0051977 You want to set the database initializer to null because Entity Framework doesn't like it when the context doesn't match the schema. If you disable that then you can setup multiple different contexts with a subset of the tables in the schema. It can also be helpful if you want to add a new column to an existing table. – QAZZY Dec 10 '17 at 03:53
97

I wrote this answer about four years ago and my opinion hasn't changed. But since then there have been significant developments on the micro-services front. I added micro-services specific notes at the end...

I'll weigh in against the idea, with real-world experience to back up my vote.

I was brought on to a large application that had five contexts for a single database. In the end, we ended up removing all of the contexts except for one - reverting back to a single context.

At first the idea of multiple contexts seems like a good idea. We can separate our data access into domains and provide several clean lightweight contexts. Sounds like DDD, right? This would simplify our data access. Another argument is for performance in that we only access the context that we need.

But in practice, as our application grew, many of our tables shared relationships across our various contexts. For example, queries to table A in context 1 also required joining table B in context 2.

This left us with a couple poor choices. We could duplicate the tables in the various contexts. We tried this. This created several mapping problems including an EF constraint that requires each entity to have a unique name. So we ended up with entities named Person1 and Person2 in the different contexts. One could argue this was poor design on our part, but despite our best efforts, this is how our application actually grew in the real world.

We also tried querying both contexts to get the data we needed. For example, our business logic would query half of what it needed from context 1 and the other half from context 2. This had some major issues. Instead of performing one query against a single context, we had to perform multiple queries across different contexts. This has a real performance penalty.

In the end, the good news is that it was easy to strip out the multiple contexts. The context is intended to be a lightweight object. So I don't think performance is a good argument for multiple contexts. In almost all cases, I believe a single context is simpler, less complex, and will likely perform better, and you won't have to implement a bunch of work-arounds to get it to work.

I thought of one situation where multiple contexts could be useful. A separate context could be used to fix a physical issue with the database in which it actually contains more than one domain. Ideally, a context would be one-to-one to a domain, which would be one-to-one to a database. In other words, if a set of tables are in no way related to the other tables in a given database, they should probably be pulled out into a separate database. I realize this isn't always practical. But if a set of tables are so different that you would feel comfortable separating them into a separate database (but you choose not to) then I could see the case for using a separate context, but only because there are actually two separate domains.

Regarding micro-services, one single context still makes sense. However, for micro-services, each service would have its own context which includes only the database tables relevant to that service. In other words, if service x accesses tables 1 and 2, and service y accesses tables 3 and 4, each service would have its own unique context which includes tables specific to that service.

I'm interested in your thoughts.

Francisco d'Anconia
  • 2,436
  • 1
  • 19
  • 25
  • 13
    I have to agree here, particularly when targeting an existing database. I am working on this problem right now, and my gut feeling so far is: 1. Having the same physical table in multiple contexts is a bad idea. 2. If we can't decide that a table belongs in one context or another, then the two contexts are not distinct enough to be logically separated. – jkerak Jan 18 '16 at 21:02
  • 3
    I would argue that, when doing CQRS, you wouldn't have any relationships between contexts (each view could have its own context) so this warning does not apply to every case when one might want to have multiple contexts. Instead of joining and referencing, use data-duplication for each context. - This does not negate the usefulness of this answer though :) – urbanhusky Jun 21 '16 at 09:23
  • 1
    I felt the pain you faced in deep ! :/ I also think one context is better choice for the simplicity. – ahmet Aug 06 '18 at 10:47
  • 1
    My only argument against, noting that I otherwise fully agree, is in regards to Identity. Especially with horizontal scaling, identity layer needs separated out in almost all cases where load balancing is introduced. At least, that's what I'm finding. – Barry Oct 22 '18 at 15:18
  • 10
    To me it seems like you did not go DDD all the way, if your aggregates needed to know other aggregates. If you need to reference something there are two reasons why: they are in the same aggregate wich means they have to be changed in the same transaction or they are not and you got your boundaries wrong. – modmoto Nov 26 '18 at 15:59
  • 1
    "This has a real performance penalty" especially when you have a separate database for every client and are using a serverless approach such as Azure Function on consumption plan. Unless you are caching connections somehow, initializing multiple db connections on every function call would be a real overhead. However, there would also be an overhead of loading the whole schema in a single context every time. – tehmas May 08 '20 at 05:53
  • 1
    according to: "But in practice, as our application grew, many of our tables shared relationships across our various contexts. For example, queries to table A in context 1 also required joining table B in context 2." -> Queries could use other Data Access Infrastructure with e.g. Dapper. DbContext, especially multiple dbcontexts are more needed in DDD – zolty13 Aug 19 '20 at 08:49
62

Distinguishing contexts by setting the default schema

In EF6 you can have multiple contexts, just specify the name for the default database schema in the OnModelCreating method of you DbContext derived class (where the Fluent-API configuration is). This will work in EF6:

public partial class CustomerModel : DbContext
{   
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.HasDefaultSchema("Customer");

        // Fluent API configuration
    }   
}

This example will use "Customer" as prefix for your database tables (instead of "dbo"). More importantly it will also prefix the __MigrationHistory table(s), e.g. Customer.__MigrationHistory. So you can have more than one __MigrationHistory table in a single database, one for each context. So the changes you make for one context will not mess with the other.

When adding the migration, specify the fully qualified name of your configuration class (derived from DbMigrationsConfiguration) as parameter in the add-migration command:

add-migration NAME_OF_MIGRATION -ConfigurationTypeName FULLY_QUALIFIED_NAME_OF_CONFIGURATION_CLASS


A short word on the context key

According to this MSDN article "Chapter - Multiple Models Targeting the Same Database" EF 6 would probably handle the situation even if only one MigrationHistory table existed, because in the table there is a ContextKey column to distinguish the migrations.

However I prefer having more than one MigrationHistory table by specifying the default schema like explained above.


Using separate migration folders

In such a scenario you might also want to work with different "Migration" folders in you project. You can set up your DbMigrationsConfiguration derived class accordingly using the MigrationsDirectory property:

internal sealed class ConfigurationA : DbMigrationsConfiguration<ModelA>
{
    public ConfigurationA()
    {
        AutomaticMigrationsEnabled = false;
        MigrationsDirectory = @"Migrations\ModelA";
    }
}

internal sealed class ConfigurationB : DbMigrationsConfiguration<ModelB>
{
    public ConfigurationB()
    {
        AutomaticMigrationsEnabled = false;
        MigrationsDirectory = @"Migrations\ModelB";
    }
}


Summary

All in all, you can say that everything is cleanly separated: Contexts, Migration folders in the project and tables in the database.

I would choose such a solution, if there are groups of entities which are part of a bigger topic, but are not related (via foreign keys) to one another.

If the groups of entities do not have anything to do which each other, I would created a separate database for each of them and also access them in different projects, probably with one single context in each project.

Martin
  • 5,165
  • 1
  • 37
  • 50
  • 1
    What do you do when you need to update 2 entities which are in different contexts? – yakya May 10 '18 at 12:55
  • I would create a new (service) class that knows boths contexts, think of a good name and the responsibilities of this class and do this update in one of its methods. – Martin Oct 19 '18 at 07:05
9

Simple example to achieve the below:

    ApplicationDbContext forumDB = new ApplicationDbContext();
    MonitorDbContext monitor = new MonitorDbContext();

Just scope the properties in the main context: (used to create and maintain the DB) Note: Just use protected: (Entity is not exposed here)

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext()
        : base("QAForum", throwIfV1Schema: false)
    {

    }
    protected DbSet<Diagnostic> Diagnostics { get; set; }
    public DbSet<Forum> Forums { get; set; }
    public DbSet<Post> Posts { get; set; }
    public DbSet<Thread> Threads { get; set; }
    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
    }
}

MonitorContext: Expose separate Entity here

public class MonitorDbContext: DbContext
{
    public MonitorDbContext()
        : base("QAForum")
    {

    }
    public DbSet<Diagnostic> Diagnostics { get; set; }
    // add more here
}

Diagnostics Model:

public class Diagnostic
{
    [Key]
    public Guid DiagnosticID { get; set; }
    public string ApplicationName { get; set; }
    public DateTime DiagnosticTime { get; set; }
    public string Data { get; set; }
}

If you like you could mark all entities as protected inside the main ApplicationDbContext, then create additional contexts as needed for each separation of schemas.

They all use the same connection string, however they use separate connections, so do not cross transactions and be aware of locking issues. Generally your designing separation so this shouldn't happen anyway.

Choco
  • 1,189
  • 1
  • 14
  • 29
  • 2
    This helped a lot. The "secondary" context does not need to declare the shared table. Just manually add it's `DbSet` definition. I do that in a partial class matching what the EF Designer makes. – Glen Little Sep 16 '15 at 21:57
  • 1
    You saved me lot of headaches, sir! You provided a concrete solution instead of the accepted answer. Really appreciated! – ˈvɔlə Apr 16 '20 at 14:49
6

Reminder: If you do combine multiple contexts make sure you cut n paste all the functionality in your various RealContexts.OnModelCreating() into your single CombinedContext.OnModelCreating().

I just wasted time hunting down why my cascade delete relationships weren't being preserved only to discover that I hadn't ported the modelBuilder.Entity<T>()....WillCascadeOnDelete(); code from my real context into my combined context.

AppleDelegate
  • 4,269
  • 1
  • 20
  • 27
Ilan
  • 1,647
  • 1
  • 15
  • 16
  • 9
    Rather than cutting and pasting, could you just call `OtherContext.OnModelCreating()` from your combined context? – Alex Feb 17 '15 at 15:37
6

Inspired by @JulieLerman 's DDD MSDN Mag Article 2013

public class ShippingContext : BaseContext<ShippingContext>
{
  public DbSet<Shipment> Shipments { get; set; }
  public DbSet<Shipper> Shippers { get; set; }
  public DbSet<OrderShippingDetail> Order { get; set; } //Orders table
  public DbSet<ItemToBeShipped> ItemsToBeShipped { get; set; }
  protected override void OnModelCreating(DbModelBuilder modelBuilder)
  {
    modelBuilder.Ignore<LineItem>();
    modelBuilder.Ignore<Order>();
    modelBuilder.Configurations.Add(new ShippingAddressMap());
  }
}

public class BaseContext<TContext>
  DbContext where TContext : DbContext
{
  static BaseContext()
  {
    Database.SetInitializer<TContext>(null);
  }
  protected BaseContext() : base("DPSalesDatabase")
  {}
}   

"If you’re doing new development and you want to let Code First create or migrate your database based on your classes, you’ll need to create an “uber-model” using a DbContext that includes all of the classes and relationships needed to build a complete model that represents the database. However, this context must not inherit from BaseContext." JL

OzBob
  • 4,227
  • 1
  • 39
  • 48
  • having e.g. the baseContext and two derived contexts (e.g. ShippingContext and SomeOtherContext). Is it possible to define queries between the derived contexts like ```_context.Shipments.Select(shipment => shipment.OtherContexts).ToList()```? And how do I need to add this baseContext into DI? – SNO Nov 19 '22 at 19:22
  • great questions. feel free to post an answer here when you have had a go yourself and found out through experimentation. you can also post a new question here on SO. The original MSDNMagazine article is here: https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/january/data-points-shrink-ef-models-with-ddd-bounded-contexts – OzBob Nov 21 '22 at 06:38
5

My gut told me the same thing when I came across this design.

I am working on a code base where there are three dbContexts to one database. 2 out of the 3 dbcontexts are dependent on information from 1 dbcontext because it serves up the administrative data. This design has placed constraints on how you can query your data. I ran into this problem where you cannot join across dbcontexts. Instead what you are required to do is query the two separate dbcontexts then do a join in memory or iterate through both to get the combination of the two as a result set. The problem with that is instead of querying for a specific result set you are now loading all your records into memory and then doing a join against the two result sets in memory. It can really slow things down.

I would ask the question "just because you can, should you?"

See this article for the problem I came across related to this design. The specified LINQ expression contains references to queries that are associated with different contexts

Community
  • 1
  • 1
Victor J. Garcia
  • 302
  • 4
  • 10
  • 3
    I have worked on a large system where we had multiple contexts. One of the things that I found was that sometimes you had to include the same DbSet in multiple contexts. On the one hand this breaks some purity concerns, but it does allow you to complete your queries. For a case where there are certain admin tables that you need to read, you could add them to a base DbContext class and inherit them in your app module contexts. You "real" admin context's purpose might be redefined as "provide maintenance for admin tables", rather than providing all access to them. – JMarsch Jun 23 '13 at 17:42
  • 2
    For what it's worth, I always went back and forth over whether it was worth it. On the one hand, with separate contexts, there is less to know for a dev who just wants to work on one module, and you feel safer defining and using custom projections (because you aren't worried about the effects it will have on other modules). On the other, you do run into some issues when you need to share data cross-context. – JMarsch Jun 23 '13 at 17:50
  • 2
    You don't HAVE to include entities in both you can always get the ids and do a 2nd query to a different context . For small systems this is bad , for larger DBs / systems with many devs coherence of multi table structures is a much bigger and more difficult problem than 2 queries . – user1496062 Jun 13 '14 at 05:02
3

Another bit of "wisdom". I have a database facing both, the internet and an internal app. I have a context for each face. That helps me to keep a disciplined, secured segregation.

Miguel Delgado
  • 133
  • 1
  • 7
2

In code first, you can have multiple DBContext and just one database. You just have to specify the connection string in the constructor.

public class MovieDBContext : DbContext
{
    public MovieDBContext()
        : base("DefaultConnection")
    {

    }
    public DbSet<Movie> Movies { get; set; }
}
Daniel
  • 9,312
  • 3
  • 48
  • 48
2

I want to share a case, where I think the possibility of having multiple DBContexts in the same database makes good sense.

I have a solution with two database. One is for domain data except user information. The other is solely for user information. This division is primarily driven by the EU General Data Protection Regulation. By having two databases, I can freely move the domain data around (e.g. from Azure to my development environment) as long as the user data stays in one secure place.

Now for the user database I have implemented two schemas through EF. One is the default one provided by the AspNet Identity framework. The other is our own implementing anything else user related. I prefer this solution over extending the ApsNet schema, because I can easily handle future changes to AspNet Identity and at the same time the separation makes it clear to the programmers, that "our own user information" goes in the specific user schema we have defined.

freilebt
  • 29
  • 3
  • 2
    I fail to see any question in my reply. I am not asking one sole question! Rather sharing a scenario where the topic of the discussion makes good sense. – freilebt Feb 26 '18 at 12:34
-1

Huh, spent quite a lot of time on a problem with separate DB contexts for each DB schema, hope that it will help somebody else...

I recently started working on a project that had one database with 3 schemas (DB first approach), one of them for user management. There was a DB context scaffolded from each separate schema. Of course, users were related to other schemas as well, eg. schema KB had a table Topic, which had "created by", "last modified by" etc. FK to identity schema, table appuser.

These objects were loaded separately in C#, firstly, topic was loaded from 1 context, then users were loaded via user IDs from the other db context - not nice, have to fix this! (similar to using multiple dbcontexts in the same database with EF 6)

First, I tried to add missing FK instructions from identity schema to KB schema, to EF modelBuilder in KB DB context. The same as if there was only 1 context, but I separated it to 2.

modelBuilder.Entity<Topic>(entity =>
{
  entity.HasOne(d => d.Creator)
    .WithMany(p => p.TopicCreator)
    .HasForeignKey(d => d.CreatorId)
    .HasConstraintName("fk_topic_app_users");

It didnt work, because kb db context didn't have any information about the user object, postgres returned error relation "AppUsers" does not exist. Select statement didnt have proper information about schema, field names etc.

I almost gave up, but then i noticed a switch "-d" when running dotnet ef dbcontext scaffold. Its short for -data-annotations - Use attributes to configure the model (where possible). If omitted, only the fluent API is used. With this switch specified, object properties were defined not in db context OnModelCreating(), but rather on the object itself, with attributes.

This way, EF got sufficient information for generating a proper SQL statement with proper field names and schemas.

TL;DR: separate DB contexts don't handle relations (FKs) between them well, each context only has information about its own entities. When specifying "-data-annotations" switch on dotnet ef dbcontext scaffold, these informations arent stored in each separate context, but on DB objects themselves.

berserker
  • 104
  • 5