我正在创建我的第一个N-Tier MVC应用程序,我遇到了如何使用我的数据库第一种方法管理多个DbContexts
的路障。
我有以下图层
Presentation
Service (WCF)
Business
Data Access
我不希望在我的服务层中使用实体框架引用,但是我没有看到如何创建接口或管理两个上下文的东西。我在IDatabaseFactory中使用了单个上下文,但我似乎找不到管理两个上下文的方法。
以下是我的服务ctor中创建的UnitOfWork
,但我看到它的每一个方面我仍然与SiteModelContainer
绑定,而实际上我有另一个背景。
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();
}
}
答案 0 :(得分:17)
为您的Factory和UnitOfWork提供通用类型参数可能是一个解决方案:
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();
}
}
IDatabaseFactory
和IUnitWork
接口也必须是通用的。
然后,您可以为不同的上下文创建工作单元:
var factory1 = new DatabaseFactory<SiteModelContainer>();
var unitOfWork1 = new UnitOfWork<SiteModelContainer>(factory1);
var factory2 = new DatabaseFactory<AnotherModelContainer>();
var unitOfWork2 = new UnitOfWork<AnotherModelContainer>(factory2);
修改强>
要摆脱服务类中对EF的依赖,您可以尝试这样的方法。该服务只知道这三个接口:
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.
}
以下是特定于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.
}
您的服务会被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();
}
}
}
创建服务时,将注入工厂的具体实例:
var service = new MyService(new UnitOfWorkFactory());
请记住,艰苦的工作将在抽象存储库及其实现中。只要您的服务类中不再具有EF上下文,就必须在repo界面中模仿许多方法,以支持操作数据的所有必要方案。
答案 1 :(得分:0)
您可以创建一个包装器,它是跨DbContexts的通用存储库(并利用底层的ObjectContext来支持它)。
以下是我过去使用过的一个示例(它还将您的代码与对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
}