Home > Blockchain >  Adding multiple DBContext in EFCore | Reader and Writer Context
Adding multiple DBContext in EFCore | Reader and Writer Context

Time:10-06

I am implementing a pattern via a demo .net 6 application. In this application, I have a DemoContext which implements the DBContext and has all the tables as DbSet<Table_Names>. I have also created 2 other classes with the name of DataReaderContext and DataWriterContext that as the name suggests, I have planned them on using for reading from the db and writing to the db respectively. Both of these classes extend the DemoDBContext class. Now since I am following the code first approach I will be adding migrations to sync my model classes and the database tables.

Now to tell you my intention behind these 2 classes I have thought of making the QueryTrackingBehavior as No tracking for the reader since its work is to only do read operations from the database. For the Writer context, I havent done anything special apart from just extending the main context class.

Now the problem comes when I am trying to add an initial migration. It says that I have multiple contexts. I found the solution that I need to pass the context name(along with the whole namespace) with -context option but in this case, should I pass the main context? or the reader context? or the writer context? since passing in the main context would mean no changes in tarcking behavior. Also will it not create issues if let's say I create migrations for both reader and writer context since the base class for them is the DemoContext and might create tables twice?

Is there something wrong in this approach or is there some other way to achieve this? Am I doing something wrong here ? I am attaching code snippets below

    public class DemoContext : DbContext
    {
        public DemoContext()
        {
        }

        public DemoContext(DbContextOptions options)
        {
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
          //connecton string configuration
        }

        //DBSets
   }
    public class DataReaderContext : DemoContext
    {
        public DataReaderContext(DbContextOptions<DataReaderContext> options) : base(options)
        {
            this.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
        }
    }
    public class DataWriterContext : DbContext
    {
        public DataWriterContext(DbContextOptions<DataWriterContext> options) : base(options)
        {
        }
    }

CodePudding user response:

The change tracking behavior is irrelevant for migrations. Just pick one DbContext to generate the migrations, probably DemoContext.

CodePudding user response:

Using separate DbContext for all read vs. write operations is somewhat overkill IMHO. That said, for certain operations it can be beneficial to split off DbContexts for organizational or performance reasons. For example if you are logging or Auditing to a database you might benefit from having that context manage the entities for those respective operations with very lightweight representations of other relatable entities rather than having everything in one large DbContext since these operations should be committed independently of updates that may be tracked on the main application DbContext. I.e. writing a log to record an exception during a save should not be "polluted" and blocked by the changes that could not be saved successfully. This is commonly referred to as "Bounded" DbContexts. In larger systems it can be beneficial to organize areas of functionality into separate DbContexts responsible for write operations for that area. (While having some awareness of other common entities for read/association purposes.)

Where I will use read-only DbContexts are in larger systems for reporting-type querying. These are often pointed at read-only replica databases where real-time data accuracy is not needed but I don't want to risk potential read locks on the primary database. Rather than having a single DbContext with an instance pointed at the replica, I will used a cut-down separate DbContext that does things like prevents SaveChanges calls. Users running expensive in-app queries are directed to this reporting DbContext. If they select a row to actually perform an action, the ID(s) are handed off to be loaded by the main DbContext against the live production database. Examples of this can be building reports, or managers running broad-reaching queries that they might want to drill down and review.

In any case if separate DbContexts are desired for a single database schema and you want that schema managed by Code-First, then one should be nominated as the Owner of the schema, and all subsidiary DbContexts would be set up as DB-First.

If you want to get the most out of read performance, I highly recommend leveraging projection rather than loading entire entity graphs (relationships) and trying to juggle between tracked and untracked queries. Effective use of projection can speed up query performance and avoid tracking bloat in EF. Also avoid anti-patterns like Generic Repository patterns which can lead to poorer performing and higher memory usage queries trying to straight-jacket applications into a "one size fits all" data access approach.

  • Related