Using multiple DbContexts with a generic repository and unit of work

c# dbcontext entity-framework repository-pattern unit-of-work

Question

My application is growing, and I only have one response so far.MyDbContext This contains every table I require for my application. I want to divide them up into multiple categories (for the purpose of overview)DbContext , likeMainDbContext , EstateModuleDbContext , AnotherModuleDbContext and UserDbContext .

I'm not sure how to do this because I currently use dependency injection (ninject) to attach my DbContext to my UnitOfWork class as follows:

kernel.Bind(typeof(IUnitOfWork)).To(typeof(UnitOfWork<MyDbContext>));

Should I abandon this strategy that involves dependency injection and explicit setting theDbContext I'd want to use these for my services:

private readonly EstateService _estateService;

public HomeController()
{
    IUnitOfWork uow = new UnitOfWork<MyDbContext>();
    _estateService = new EstateService(uow);
}

in place of

private readonly EstateService _estateService;

public HomeController(IUnitOfWork uow)
{
    _estateService = new EstateService(uow);
}

Or would a different strategy work better here? Additionally, on a side note, I dislike taking theuow Is there another (better) way to go about this, please?

Code

MyDbContext and IDbContext are both present.

public interface IDbContext
{
    DbSet<T> Set<T>() where T : class;

    DbEntityEntry<T> Entry<T>(T entity) where T : class;

    int SaveChanges();

    void Dispose();
}

public class MyDbContext : DbContext, IDbContext
{
    public DbSet<Table1> Table1 { get; set; }
    public DbSet<Table2> Table1 { get; set; }
    public DbSet<Table3> Table1 { get; set; }
    public DbSet<Table4> Table1 { get; set; }
    public DbSet<Table5> Table1 { get; set; }
    /* and so on */

    static MyDbContext()
    {
        Database.SetInitializer<MyDbContext>(new CreateDatabaseIfNotExists<MyDbContext>());
    }

    public MyDbContext()
        : base("MyDbContext")
    {
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {

    }
}

The implementation follows, and I have this IRepository:

public interface IRepository<T> where T : class
{
    IQueryable<T> GetAll();

    void Add(T entity);

    void Delete(T entity);

    void DeleteAll(IEnumerable<T> entity);

    void Update(T entity);

    bool Any();
}

public class Repository<T> : IRepository<T> where T : class
{
    private readonly IDbContext _context;
    private readonly IDbSet<T> _dbset;

    public Repository(IDbContext context)
    {
        _context = context;
        _dbset = context.Set<T>();
    }

    public virtual IQueryable<T> GetAll()
    {
        return _dbset;
    }

    public virtual void Add(T entity)
    {
        _dbset.Add(entity);
    }

    public virtual void Delete(T entity)
    {
        var entry = _context.Entry(entity);
        entry.State = EntityState.Deleted;
        _dbset.Remove(entity);
    }

    public virtual void DeleteAll(IEnumerable<T> entity)
    {
        foreach (var ent in entity)
        {
            var entry = _context.Entry(ent);
            entry.State = EntityState.Deleted;
            _dbset.Remove(ent);
        }
    }

    public virtual void Update(T entity)
    {
        var entry = _context.Entry(entity);
        _dbset.Attach(entity);
        entry.State = EntityState.Modified;
    }

    public virtual bool Any()
    {
        return _dbset.Any();
    }
}

Additionally, the implementation and IUnitOfWork that manage the DbContext-related work

public interface IUnitOfWork : IDisposable
{
    IRepository<TEntity> GetRepository<TEntity>() where TEntity : class;

    void Save();
}

public class UnitOfWork<TContext> : IUnitOfWork where TContext : IDbContext, new()
{
    private readonly IDbContext _ctx;
    private readonly Dictionary<Type, object> _repositories;
    private bool _disposed;

    public UnitOfWork()
    {
        _ctx = new TContext();
        _repositories = new Dictionary<Type, object>();
        _disposed = false;
    }

    public IRepository<TEntity> GetRepository<TEntity>() where TEntity : class
    {
        // Checks if the Dictionary Key contains the Model class
        if (_repositories.Keys.Contains(typeof(TEntity)))
        {
            // Return the repository for that Model class
            return _repositories[typeof(TEntity)] as IRepository<TEntity>;
        }

        // If the repository for that Model class doesn't exist, create it
        var repository = new Repository<TEntity>(_ctx);

        // Add it to the dictionary
        _repositories.Add(typeof(TEntity), repository);

        return repository;
    }

    public void Save()
    {
        _ctx.SaveChanges();
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (this._disposed) return;

        if (disposing)
        {
            _ctx.Dispose();
        }

        this._disposed = true;
    }
} 
1
4
12/12/2013 6:22:36 PM

Accepted Answer

Avoid dividing your modular data into several sections.DbContext s unless there are compelling reasons to do otherwise. individuals fromDbContextA Entities in a collection cannot have automatic navigation or collection characteristics.DbContextB If the context was divided, your code would be in charge of manually applying restrictions and loading relevant data between contexts.

You can still arrange your CLR code and database tables by module for the "sake of overview" (also known as maintaining your sanity). Maintain the POCOs in several directories with various namespaces. You can group tables based on schema. (However, when arranging by SQL schema, you should probably also take security into account. Design the schemas in accordance with any rules that specify which database users should have restricted access to specific tables, for instance.) When creating the model, you can then do this:

ToTable("TableName", "SchemaName"); // put table under SchemaName, not dbo

only choose an independentDbContext when its constituents don't interact with any other entities in your firstDbContext .

I share Wiktor's dislike of your interface and implementation design. I particularly dislikepublic interface IRepository<T> . Why declare several alsopublic DbSet<TableN> TableN { get; set; } in theMyDbContext ? Please read this piece and then like as as a favor to me.

With an EF interface architecture like this, your code can be much simplified:

interface IUnitOfWork
{
    int SaveChanges();
}
interface IQueryEntities
{
    IQueryable<T> Query<T>(); // implementation returns Set<T>().AsNoTracking()
    IQueryable<T> EagerLoad<T>(IQueryable<T> queryable, Expression<Func<T, object>> expression); // implementation returns queryable.Include(expression)
}
interface ICommandEntities : IQueryEntities, IUnitOfWork
{
    T Find<T>(params object[] keyValues);
    IQueryable<T> FindMany<T>(); // implementation returns Set<T>() without .AsNoTracking()
    void Create<T>(T entity); // implementation changes Entry(entity).State
    void Update<T>(T entity); // implementation changes Entry(entity).State
    void Delete<T>(T entity); // implementation changes Entry(entity).State
    void Reload<T>(T entity); // implementation invokes Entry(entity).Reload
}

If you stateMyDbContext : ICommandEntities only a few methods need to be set up in order to implement the interface (usually one-liners). Any of the following 3 interfaces can then be injected into your service implementations: usuallyICommandEntities for procedures that result in adverse effects, andIQueryEntities for activities which don't. Any services (or service decorators) whose sole responsibility is state preservation may become dependent onIUnitOfWork I reject that.Controller s should adopt a reliance onIUnitOfWork however. Your services should store modifications using the aforementioned design before returning to theController .

if having several distinctDbContext You can follow Wiktor's advice and make the aforementioned interfaces generic if any of the classes in your program make sense. After that, you may inject dependencies into services as follows:

public SomeServiceClass(IQueryEntities<UserEntities> users,
    ICommandEntities<EstateModuleEntities> estateModule) { ... }

public SomeControllerClass(SomeServiceClass service) { ... }

// Ninject will automatically constructor inject service instance into controller
// you don't need to pass arguments to the service constructor from controller

Wide per-aggregate repository interfaces, or even worse, interfaces per entity, can conflict with EF, multiply tedious plumbing code, and over-inject your constructors. Give your services greater freedom instead. techniques like.Any() are not appropriate for the interface, as you may just call extensions on theIQueryable<T> delivered byQuery<T> or FindMany<T> from inside your service procedures.

10
8/11/2014 11:48:24 AM

Popular Answer

Although the implementation is general, your unit of work interface is not. Making a choice and adhering to the same convention would be the simplest approach to fix issue.

Make your UI, for instance, generic as well. In this manner, three separate implementations might be registered with three different interfaces (the same interface with three different generic parameters):

 container.Bind( typeof<IUnitOfWork<ContextOne>> ).To( typeof<UnitOfWork<ContextOne>> );
 ...

And certainly, injecting your unit of labor into controllers or services is a fantastic idea.



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow