Derviş Kayımbaşıoğlu
Derviş Kayımbaşıoğlu

Reputation: 30625

Splitting DbContext according to

I would like to split into pieces my DbContext class so that each module has its own class which I believe makes working easier, more error throne and lowers code complexity.

I believe that I saw it somewhere in the past while googling however no luck of finding it.

My DbContext is in Infrastructure Layer, and My classes are in Domain Context. That is not problem. I would like to separate mappings and configurations into separate classes. My DbContext will remain same except it will be splitted.

I marked the code peaces I am willing to split into pieces below:

public class WestCoreDbContext : DbContext
{
    public WestCoreDbContext(DbContextOptions<WestCoreDbContext> options) : base(options)
    {

    }
    #region WOULD LIKE TO SPLIT THIS PART
    public virtual DbSet<SoftwareTest> SoftwareTests { get; set; }
    public virtual DbSet<SoftwareTestCase> SoftwareTestCases { get; set; }
    public virtual DbSet<SoftwareTestCaseStep> SoftwareTestCaseSteps { get; set; }
    public virtual DbSet<SoftwareTestCaseStepResult> SoftwareTestCaseStepResults { get; set; }

    public virtual DbSet<Position> Positions { get; set; }
    #endregion

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {

        #region WOULD LIKE TO SPLIT THIS PART
        SoftwareTestMapping(modelBuilder);
        SoftwareTestCaseMapping(modelBuilder);
        SoftwareTestCaseMapping(modelBuilder);
        SoftwareTestCaseStepMapping(modelBuilder);
        SoftwareTestCaseStepResultsMapping(modelBuilder);
        PositionMapping(modelBuilder);

        RelationshipsMapping(modelBuilder);
        #endregion

        modelBuilder.MyOracleNamingConventions();

        base.OnModelCreating(modelBuilder);
    }

    #region WOULD LIKE TO SPLIT THIS PART

    private void SoftwareTestMapping(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<SoftwareTest>();
    }

    private void SoftwareTestCaseMapping(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<SoftwareTestCase>();
    }

    private void SoftwareTestCaseStepMapping(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<SoftwareTestCaseStep>();
    }

    private void PositionMapping(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Position>();
    }

    private void SoftwareTestCaseStepResultsMapping(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<SoftwareTestCaseStepResult>();
    }

    private void RelationshipsMapping(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<SoftwareTest>().HasMany(x => x.SoftwareTestCases).WithOne(op => op.SoftwareTest).IsRequired().HasForeignKey(@"StId");
        modelBuilder.Entity<SoftwareTestCase>().HasOne(x => x.SoftwareTest).WithMany(op => op.SoftwareTestCases).IsRequired().HasForeignKey(@"StId");

        modelBuilder.Entity<SoftwareTestCase>().HasMany(x => x.SoftwareTestCaseSteps).WithOne(op => op.SoftwareTestCase).IsRequired().HasForeignKey("StcId");
        modelBuilder.Entity<SoftwareTestCaseStep>().HasOne(x => x.SoftwareTestCase).WithMany(op => op.SoftwareTestCaseSteps).IsRequired().HasForeignKey("StcId");

        modelBuilder.Entity<SoftwareTestCaseStep>().HasMany(x => x.SoftwareTestCaseStepResults).WithOne(op => op.SoftwareTestCaseStep).IsRequired().HasForeignKey("StcsId");
        modelBuilder.Entity<SoftwareTestCaseStepResult>().HasOne(x => x.SoftwareTestCaseStep).WithMany(op => op.SoftwareTestCaseStepResults).IsRequired().HasForeignKey("StcsId");
    }
    #endregion

    public bool HasChanges()
    {
        return ChangeTracker.Entries().Any(e => e.State == EntityState.Added || e.State == EntityState.Modified || e.State == EntityState.Deleted);
    }

    ...
}
}

I dont know exact way of splitting DbContext. I need a way for doing this. can you provide me solution?

Upvotes: 4

Views: 4141

Answers (2)

Usama Tabbassum
Usama Tabbassum

Reputation: 49

You can use EF Core power tool and that will handle automatically. enter image description here

Upvotes: 0

Sefe
Sefe

Reputation: 14007

You first have o be clear what you mean with split the DbContext. You say that it is a separation of concerns issue, so you should first decide which concerns you want to separate. The DbContext handles the concern of mapping the database table to an object model and so it makes sense that it does everything related to that. The other end of separation of concerns is to not split a concern into pieces since that too increases program complexity.

A good separation of concerns is to keep the DbContext together and let it do the basic work of mapping the DB to your object model. Additional tasks, like specialized queries to the DB would be encapsualted in repositories that would use the DbContext, which also allows you to abstract your business layer from the actual entity framework implementation.

On top of that there are still some ways you can "split" your DbContext into pieces:

Make the DbContext a partial class

You can make WestCoreDbContext a partial class and split its contents over multiple code files. You should be clear though that the compiled output will be exactly the same, so you don't really improve separation of concerns in an OOP sense. You can also argue if splitting a single class to multiple code files without a good reason (like the class being partially auto-generated) will actually increase or decrease manageablity of your code. This is a decision you have to make for yourself.

Use entity type configuration classes

You can configure your entity classes with the fluent API using classes derived from EntityTypeConfiguration (or implementing the IEntityTypeConfiguration interface in .net Core 2.0), which let you to configure your entity classes outside the DbContext. This can actually help manageability when you have a larger amount of entity classes, where the OnModelCreating method would become very long.

In your case this would look like this in .net Core 2.0 (I have simplified your example):

public class WestCoreDbContext : DbContext
{
    public WestCoreDbContext(DbContextOptions<WestCoreDbContext> options) : base(options)
    {

    }

    public virtual DbSet<SoftwareTestCase> SoftwareTestCases { get; set; }
    //Define further DbSets

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.ApplyConfiguration(new SoftwareTestCaseConfiguration());
        //Apply further configurations

        modelBuilder.MyOracleNamingConventions();

        base.OnModelCreating(modelBuilder);
    }

    public bool HasChanges()
    {
        return ChangeTracker.Entries().Any(e => e.State == EntityState.Added || e.State == EntityState.Modified || e.State == EntityState.Deleted);
    }
}

//This configuration class is separated from the WestCoreDbContext and can go into a separate code file
internal class SoftwareTestCaseConfiguration : IEntityTypeConfiguration<SoftwareTestCase>
{
    public void Configure(EntityTypeBuilder<SoftwareTestCase> modelBuilder)
    {
        modelBuilder.Entity<SoftwareTestCase>().HasOne(x => x.SoftwareTest).WithMany(op => op.SoftwareTestCases).IsRequired().HasForeignKey(@"StId");
        modelBuilder.Entity<SoftwareTestCase>().HasMany(x => x.SoftwareTestCaseSteps).WithOne(op => op.SoftwareTestCase).IsRequired().HasForeignKey("StcId");
    }
}

If you are not on .net Core 2.0, check this Q&A to achieve the same result.

Upvotes: 10

Related Questions