Multiple DbContexts in an N-Level Application

I am building my first MVC N-Tier application, and I came across a road block on how to manage multiple DbContexts using the first database approach.

I have the following layers

 Presentation Service (WCF) Business Data Access 

I don’t need a reference to the entity infrastructure at my service level, but I don’t see how to create an interface or something to manage two contexts. I work with one context based on IDatabaseFactory, but I cannot find an approach to managing two.

Below is my UnitOfWork , which is created in my ctor service, but every time I look at it, I'm still attached to the SiteModelContainer , when in fact I have a different context.

 public class UnitOfWork : IUnitOfWork { private SiteModelContainer _context; private readonly IDatabaseFactory _databaseFactory; protected SiteModelContainer SiteContext { get { return _context ?? (_context = _databaseFactory.Get()); } } public UnitOfWork(IDatabaseFactory factory) { _databaseFactory = factory; _context = _databaseFactory.Get(); } //More code } public class DatabaseFactory : Disposable, IDatabaseFactory { private SiteModelContainer _dataContext; public SiteModelContainer Get() { return _dataContext ?? (_dataContext = new SiteModelContainer()); } protected override void DisposeCore() { if (_dataContext != null) _dataContext.Dispose(); } } 
+7
c # repository unit-of-work dbcontext
source share
2 answers

Providing Factory and UnitOfWork with a type parameter can be a solution:

 public class UnitOfWork<T> : IUnitOfWork<T> where T : DbContext, new() { private T _context; private readonly IDatabaseFactory<T> _databaseFactory; protected T Context { get { return _context ?? (_context = _databaseFactory.Get()); } } public UnitOfWork(IDatabaseFactory<T> factory) { _databaseFactory = factory; _context = _databaseFactory.Get(); } //More code } public class DatabaseFactory<T> : Disposable, IDatabaseFactory<T> where T : DbContext, new() { private T _dataContext; public T Get() { return _dataContext ?? (_dataContext = new T()); } protected override void DisposeCore() { if (_dataContext != null) _dataContext.Dispose(); } } 

The interfaces IDatabaseFactory and IUnitWork must also be shared.

Then you can create a Work Unit for different contexts:

 var factory1 = new DatabaseFactory<SiteModelContainer>(); var unitOfWork1 = new UnitOfWork<SiteModelContainer>(factory1); var factory2 = new DatabaseFactory<AnotherModelContainer>(); var unitOfWork2 = new UnitOfWork<AnotherModelContainer>(factory2); 

Edit:

To get rid of the EF dependency in your service classes, you can try something like this. The service knows only these three interfaces:

 public interface IUnitOfWorkFactory { IUnitOfWork Create(string contextType); } public interface IUnitOfWork : IDisposable { IRepository<TEntity> CreateGenericRepository<TEntity>() where TEntity : class; void Commit(); } public interface IRepository<T> { IQueryable<T> Find(Expression<Func<T, bool>> predicate); void Attach(T entity); void Add(T entity); // etc. } 

Here are special implementations specific to EF:

 public class UnitOfWorkFactory : IUnitOfWorkFactory { public IUnitOfWork Create(string contextType) { switch (contextType) { case "SiteModelContainer": return new UnitOfWork<SiteModelContainer>(); case "AnotherModelContainer": return new UnitOfWork<AnotherModelContainer>(); } throw new ArgumentException("Unknown contextType..."); } } public class UnitOfWork<TContext> : IUnitOfWork where TContext : DbContext, new() { private TContext _dbContext; public UnitOfWork() { _dbContext = new TContext(); } public IRepository<TEntity> CreateGenericRepository<TEntity>() where TEntity : class { return new Repository<TEntity>(_dbContext); } public void Commit() { _dbContext.SaveChanges(); } public void Dispose() { _dbContext.Dispose(); } } public class Repository<T> : IRepository<T> where T : class { private DbContext _dbContext; private DbSet<T> _dbSet; public Repository(DbContext dbContext) { _dbContext = dbContext; _dbSet = dbContext.Set<T>(); } public IQueryable<T> Find(Expression<Func<T, bool>> predicate) { return _dbSet.Where(predicate); } public void Attach(T entity) { _dbSet.Attach(entity); } public void Add(T entity) { _dbSet.Add(entity); } // etc. } 

Your service will receive IUnitOfWorkFactory :

 public class MyService { private IUnitOfWorkFactory _factory; public MyService(IUnitOfWorkFactory factory) { _factory = factory; } public MyMethod() { using(var unitOfWork1 = _factory.Create("SiteModelContainer")) { var repo1 = unitOfWork1. CreateGenericRepository<SomeEntityTypeInSiteModel>(); // Do some work unitOfWork1.Commit(); } using(var unitOfWork2 = _factory.Create("AnotherModelContainer")) { var repo2 = unitOfWork2. CreateGenericRepository<SomeEntityTypeInAnotherModel>(); // Do some work unitOfWork2.Commit(); } } } 

When a service is created, a specific Factory instance is entered:

 var service = new MyService(new UnitOfWorkFactory()); 

Keep in mind that the hard work will be in the abstract repository and its implementation. Once you no longer have the EF context in the service class, you must imitate the many methods in the repo interface that supports all the necessary scripts for managing data.

+15
source share

You can create a wrapper that is a common repository in DbContexts (and uses the underlying ObjectContext to support this).

Here is an example I used in the past (which also separates your code from any direct dependency on the Entity Framework).

 // Make your DbContext inherit from this. This goes in your Unit of Work. public interface IEntitySetProvider : IDisposable { IEntitySet<T> CreateEntitySet<T>(); } // This is your adapted DBContext public class MyDbContext1 : DbContext, IEntitySetProvider { public IEntitySet<T> CreateEntitySet<T>() { return new EntitySet<T>(((IObjectContextAdapter)this).CreateObjectSet<T>()); } . . . } /// <summary> /// A wrapper for an IQueryable that exposes AddNew and Attach methods. /// </summary> /// <typeparam name = "T"></typeparam> public interface IEntitySet<T> : IQueryable<T> { /// <summary> /// Attaches the specified value and considers it new. /// </summary> /// <param name = "value">The value.</param> void AddNew(T value); /// <summary> /// Attaches the specified value and considers it modified. /// </summary> /// <param name = "value">The value.</param> void Attach(T value); } /// <summary> /// An IEntitySet for Entity Framework. /// </summary> /// <typeparam name = "T"></typeparam> internal class EntitySet<T> : IEntitySet<T> where T : class { private readonly ObjectSet<T> _objectSet; public EntitySet(ObjectSet<T> objectSet) { _objectSet = objectSet; } #region IEntitySet<T> Members public void AddNew(T value) { _objectSet.AddObject(value); } public void Attach(T value) { _objectSet.Attach(value); _objectSet.Context.ObjectStateManager.ChangeObjectState(value, EntityState.Modified); } public IEnumerator<T> GetEnumerator() { return ((IQueryable<T>) _objectSet).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IQueryable) _objectSet).GetEnumerator(); } public Type ElementType { get { return ((IQueryable<T>) _objectSet).ElementType; } } public Expression Expression { get { return ((IQueryable<T>) _objectSet).Expression; } } public IQueryProvider Provider { get { return ((IQueryable<T>) _objectSet).Provider; } } #endregion } 
0
source share

All Articles