我已经浏览了网页,寻找一个良好的实现仓库/单位的工作模式使用实体框架。我遇到的每件事都是在抽象的某个时刻紧密耦合的,或者假设工作单元和存储库使用的DbContext是共享的,并且应该存在于整个HTTP请求中(例如,通过依赖注入每个请求)。
例如,假设您正在使用来自服务层的存储库,服务构造函数可能如下所示:
public DirectoryService(IUnitOfWork unitOfWork, ICountryRepository countryRepo, IProvinceRepository provinceRepo)
{
/* set member variables... */
}工作单元构造函数可能如下所示:
public UnitOfWork(IDbContext context)
{
_context = context;
}存储库构造函数可能如下所示:
CountryRepository(IDbContext context)
{
_context = context;
}该解决方案使人盲目地假设依赖注入正在设置工作单元和存储库,以便使用每个请求的实例共享相同的IDbContext。这真的是一个安全的假设吗?
如果您使用的依赖注入与每个请求的实例,相同的IDbContext将注入到多个工作单位。工作单位不再是原子的,是吗?我可能在一个服务中有挂起的更改,然后提交到另一个服务中,因为上下文是跨多个工作单元共享的。
在我看来,设置一个IDbContextFactory并通过每个工作单元获得一个新的数据库上下文似乎更有意义。
public interface IDbContextFactory
{
IDbContext OpenContext();
}
public class UnitOfWork
{
private IDbContextFactory _factory;
private IDbContext _context;
UnitOfWork(IDbContextFactory factory)
{
_factory = factory;
}
internal IDbContext Context
{
get { return _context ?? (_context = _factory.OpenContext()); }
}
}然后,问题就变成了,如何将我的工作实现单元提供给注入的存储库?我不想假设每个请求都有实例,因为那样我就回到了我开始时的同一条船上。
我唯一能想到的就是遵循实体框架的领导,将存储库(IDbSet<T>)作为工作单元(DbContext)的一部分。
所以我可能有这样的工作单位:
public class DirectoryUnitOfWork : IDirectoryUnitOfWork
{
private IDbContextFactory _factory;
private IDbContext _context;
public DirectoryUnitOfWork(IDbContextFactory factory)
{
_factory = factory;
}
protected IDbContext Context
{
get { return _context ?? (_context = _factory.OpenContext()); }
}
public ICountryRepository CountryRepository
{
get { return _countryRepo ?? (_countryRepo = new CountryRepository(Context)); }
}
public IProvinceRepository ProvinceRepository
{
get { return _provinceRepo ?? (_provinceRepo = new ProvinceRepository(Context)); }
}
void Commit()
{
Context.SaveChanges();
}
}然后我的目录服务开始像这样
public class DirectoryService : IDirectoryService
{
private IDirectoryUnitOfWork _unitOfWork;
public DirectoryService(IDirectoryUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public GetCountry(int id)
{
return _unitOfWork.CountryRepository.GetById(id);
}
public GetProvince(int id)
{
return _unitOfWork.ProvinceRepository.GetById(id);
}
public void AddProvince(Province province)
{
_unitOfWork.ProvinceRepository.Create(province);
Country country = GetCountry(province.CountryId);
country.NumberOfProvinces++; // Update aggregate count
_unitOfWork.Commit();
}
/* ... and so on ... */
}这似乎是一项很大的工作,但是使用这种方法可以使所有的东西都松耦合,并且可以测试单元。我是不是错过了一种更简单的方法,还是说这是一种很好的方法-- if --我要抽象化实体框架?
发布于 2014-06-02 06:55:06
或者假设工作单元和存储库使用的DbContext是共享的,并且应该在整个HTTP中运行
这显然是错误的。假设上下文在UoW和存储库之间共享,并不意味着上下文生存期应该依赖于HTTP。相反,您可以创建新的上下文实例和随时使用它的UoW。这只是一个为HTTP请求而存在的默认 UoW的方便,但是创建新的本地工作单元可能很方便。
另一方面,如果存储库是从UoW公开的:
public class UnitOfWork
{
...
public IUserRepository UserRepo
{
get { ... }
}
public IAccountRepository AccountRepo
{
get { ... }
}那么,在repos之间共享相同上下文的(而不是)可能会产生意想不到的结果:
UoW uow = ....
var u1 = uow.User.FirstOrDefault( u => u.ID == 5 );
var u2 = uow.Account.FirstOrDefault( a => a.ID_USER == 5 ).User;您肯定希望这两个查询返回id 5用户的同一个实例,而且,共享相同的上下文意味着第二个查询可以从第一级缓存中检索用户。另一方面,两个repos的两个不同上下文意味着您得到了两个不同的实例。
这也意味着这是不可能的。
var u1 = uow.User.FirstOrDefault( u => u.ID == 5 );
var a1 = uow.Account.FirstOrDefault( a => a.ID == 177 );
a1.User = u1; // oops!因为混合来自不同环境的粒子只会引起一个例外。但是,上面的场景是常见的!
这些观察得出的结论是,应该在repos之间共享上下文。但是,如果需要一个新实例,只需创建上下文的本地新实例,将其注入UoW,然后将其注入repos,并随意释放它。
https://stackoverflow.com/questions/23987471
复制相似问题