15 c# entity-framework dependency-injection unit-of-work autofac
我用实体框架6开发Web应用程序,并且在设计应用程序结构时遇到了困难.我的主要问题是如何在我的特定情况下处理依赖注入.
下面的代码是我希望应用程序的样子.我正在使用Autofac,但我想这对每个DI用户来说都足够了解:
public interface IUnitOfWork
{
bool Commit();
}
public class UnitOfWork : IUnitOfWork, IDisposable
{
private DbContext _context;
public UnitOfWork(DbContext context)
{
_context = context;
}
public bool Commit()
{
// ..
}
public bool Dispose()
{
_context.Dispose();
}
}
public class ProductsController : ApiController
{
public ProductsController(IProductsManager managet)
}
public class ProductsManager : IProductsManager
{
private Func<Owned<IUnitOfWork>> _uowFactory;
private IProductsDataService _dataService;
public Manager(Func<Owned<IUnitOfWork>> uowFactory, IProductsDataService dataService)
{
_dataService = dataService;
_uowFactory = uowFactory;
}
public bool AddProduct(ProductEntity product)
{
using (ownedUow = _uowFactory())
{
var uow = ownedUow.Value;
var addedProduct = _dataService.AddProduct(product);
if (addedProduct != null)
uow.Commit();
}
}
}
public interface IProductsDataService
{
ProductEntity AddProduct (Product product)
}
public class ProductsDataService : IProductsDataService
{
private IRepositoriesFactory _reposFactory;
public DataService(IRepositoriesFactory reposFactory)
{
_reposFactory = reposFactory;
}
public ProductEntity AddProduct(ProductEntity product)
{
var repo = reposFactory.Get<IProductsRepository>();
return repo.AddProduct(product);
}
}
public interface IRepositoriesFactory
{
T Get<T>() where T : IRepository
}
public class RepositoriesFactory : IRepositoriesFactory
{
private ILifetimeScope _scope;
public RepositoriesFactory(ILifetimeScope scope)
{
_scope = scope;
}
public T Get<T>() where T : IRepository
{
return _scope.Resolve<T>();
}
}
public interface IProductsRepository
{
ProductEntity AddProduct(ProductEntity);
}
public ProductsRepository : IProductsRepository
{
private DbContext _context;
public ProductsRepository(DbContext context)
{
_context = context;
}
public ProductEntity AddProduct(ProductEntity)
{
// Implementation..
}
}
Run Code Online (Sandbox Code Playgroud)
这是我认为理想的实现,但是我不知道如何实现这一点,因为我的ProductsDataService是单例,因此它与工厂工厂单元创建的Owned范围无关.有没有办法可以将要创建的存储库关联起来,并将与工作单元创建的DbContext相同的DbContext接受?以某种方式更改RepositoriesFactory中的代码?
目前我所拥有的是工作单元包含存储库工厂,以便存储库中的上下文与工作单元中的上下文相同(我按照范围注册DbContext),此时管理器执行DataService的工作也是我不喜欢的.
我知道我可以将UnitOfWork - 方法注入传递给DataService方法,但我宁愿使用Ctor注入,因为在我看来它看起来更好.
我想要的是分离它 - 一个管理器,它的工作是实例化工作单元并在需要时提交它们,以及另一个实际执行逻辑的类(DataService).
无论如何,如果您有任何改进意见/想法,我想听听您对此实施的意见.
谢谢你的时间!
编辑:这是我最终得到的:
public interface IUnitOfWork
{
bool Commit();
}
public class DatabaseUnitOfWork : IUnitOfWork
{
private DbContext _context;
public DatabaseUnitOfWork(DbContext context)
{
_context = context;
}
public bool Commit()
{
// ..
}
}
// Singleton
public class ProductsManager : IProductsManager
{
private Func<Owned<IProductsDataService>> _uowFactory;
public ProductsManager(Func<Owned<IProductsDataService>> uowFactory)
{
_uowFactory = uowFactory;
}
public bool AddProduct(ProductEntity product)
{
using (ownedUow = _uowFactory())
{
var dataService = ownedUow.Value;
var addedProduct = _dataService.AddProduct(product);
if (addedProduct != null)
uow.Commit();
}
}
}
public interface IProductsDataService : IUnitOfWork
{
ProductEntity AddProduct (Product product)
}
public class ProductsDataService : DatabaseUnitOfWork, IDataService
{
private IRepositoriesFactory _reposFactory;
public DataService(IRepositoriesFactory reposFactory)
{
_reposFactory = reposFactory;
}
public ProductEntity AddProduct(ProductEntity product)
{
var repo = _reposFactory .Get<IProductsRepository>();
return repo.AddProduct(product);
}
}
public interface IRepositoriesFactory
{
Get<T>() where T : IRepository
}
public class RepositoriesFactory : IRepositoriesFactory
{
private ILifetimeScope _scope;
public RepositoriesFactory(ILifetimeScope scope)
{
_scope = scope;
}
public Get<T>() where T : IRepository
{
return _scope.Resolve<T>();
}
}
public interface IProductsRepository
{
ProductEntity AddProduct(ProductEntity);
}
public ProductsRepository : IProductsRepository
{
private DbContext _context;
public ProductsRepository(DbContext context)
{
_context = context;
}
public ProductEntity AddProduct(ProductEntity)
{
// Implementation..
}
}
Run Code Online (Sandbox Code Playgroud)
我同意布鲁诺·加西亚关于您的代码问题的建议。但是,我发现它还存在一些其他问题。
首先我要说的是,我没有像您那样明确使用工作单元模式,但我确实理解您的目的。
布鲁诺没有遇到的问题是关注点分离不佳。他暗示了一点,我将解释更多:您的控制器中有两个独立的竞争对象,两者都试图利用相同的资源(DbContext)。正如他所说,您想要做的是为每个请求只有一个 DbContext。但这有一个问题:在处理 UnitOfWork 后,没有什么可以阻止 Controller 尝试继续使用 ProductsRepository。如果这样做,则与数据库的连接已被释放。
因为您有两个对象需要使用相同的资源,所以您应该重新构建它,其中一个对象封装另一个对象。这还带来了额外的好处,即向控制器隐藏任何有关数据传播的担忧。
控制器应该知道的只是您的服务对象,该对象应该包含所有业务逻辑以及通往存储库及其工作单元的网关,同时保持它对服务的使用者不可见。这样,控制器只需处理和处置一个对象。
解决这个问题的其他方法之一是让 ProductsRepository 从 UnitOfWork 派生,这样您就不必担心任何重复的代码。
然后,在您的AddProduct方法内部,您将_context.SaveChanges()在将该对象沿着管道返回到控制器之前进行调用。
更新(大括号的样式是为了紧凑)
这是您想要执行的操作的布局:
UnitOfWork 是最底层,包括与数据库的连接。然而,这样做是abstract因为您不想允许它的具体实现。您不再需要该接口,因为您在Commit方法中所做的操作永远不应该被公开,并且对象的保存应该在方法中完成。我将展示如何进行。
public abstract class UnitOfWork : IDisposable {
private DbContext _context;
public UnitOfWork(DbContext context) {
_context = context;
}
protected bool Commit() {
// ... (Assuming this is calling _context.SaveChanges())
}
public bool Dispose() {
_context.Dispose();
}
}
Run Code Online (Sandbox Code Playgroud)
您的存储库是下一层。派生它,UnitOfWork以便它继承所有行为,并且对于每种特定类型都相同。
public interface IProductsRepository {
ProductEntity AddProduct(ProductEntity product);
}
public ProductsRepository: UnitOfWork, IProductsRepository {
public ProductsRepository(DbContext context) : base(context) { }
public ProductEntity AddProduct(ProductEntity product) {
// Don't forget to check here. Only do that where you're using it.
if (product == null) {
throw new ArgumentNullException(nameof(product));
}
var newProduct = // Implementation...
if (newProduct != null) {
Commit();
}
return newProduct;
}
}
Run Code Online (Sandbox Code Playgroud)
完成此操作后,您现在所关心的只是拥有您的 ProductsRepository 了。在您的 DataService 层中,利用依赖注入并仅传递 ProductsRepository 本身。如果您确实决定使用工厂,则传递工厂,但让您的成员变量仍然是IProductsRepository. 不要让每个方法都必须弄清楚这一点。
不要忘记让所有接口派生自IDisposable
public interface IProductsDataService : IDisposable {
ProductEntity AddProduct(ProductEntity product);
}
public class ProductsDataService : IProductsDataService {
private IProductsRepository _repository;
public ProductsDataService(IProductsRepository repository) {
_repository = repository;
}
public ProductEntity AddProduct(ProductEntity product) {
return _repository.AddProduct(product);
}
public bool Dispose() {
_repository.Dispose();
}
}
Run Code Online (Sandbox Code Playgroud)
如果您执意要使用ProductsManager,也可以,但它只是另一层,不再提供太多好处。那个班级也有同样的待遇。
我将按照我的意愿完成您的控制器。
public class ProductsController : Controller {
private IProductsDataService _service;
public ProductsController(IProductsDataService service) {
_service = service;
}
protected override void Dispose(bool disposing) {
_service.Dispose();
base.Dispose(disposing);
}
// Or whatever you're using it as.
[HttpPost]
public ActionResult AddProduct(ProductEntity product) {
var newProduct = _service.AddProduct(product);
return View(newProduct);
}
}
Run Code Online (Sandbox Code Playgroud)