EF 6和依赖注入设计问题的工作单元

时间:2016-04-09 15:19:34

标签: c# entity-framework dependency-injection autofac unit-of-work

我使用实体框架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..
    }
}

这是我觉得理想的实现,但是我不知道如何实现这一点,因为我的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..
    }
}

2 个答案:

答案 0 :(得分:4)

您不希望在单例实例中使用单例DbContext。这没关系,可以在工厂完成。此外,您想要分享此DbContext。这也没关系,您可以在工厂解决并返回DbContext相关的续航时间。 问题是;您希望在单个实例中共享非单例DbContext而不管理生命周期(Tcp / Ip请求)。

ProductServiceProductManager是单身人士的原因是什么? 我建议您在每个lifetimescope上使用ProductServiceProductManager。当你有http请求它没关系。当您有tcp / ip请求时,您可以开始新的生命周期范围(尽可能为最高级别),然后在那里解析ProductManager

更新:我在评论中提到的解决方案1的代码。

Managers必须是单身人士(正如你所说)。

managers以外,您应将dbcontextservicesrepositoriesUow注册为per lifetime范围。

我们可以这样宣传:

public class ProductsManager : IProductsManager
    {
        //This is kind of service locator. We hide Uow and ProductDataService dependencies.
        private readonly ILifetimeScope _lifetimeScope;

        public ProductsManager(ILifetimeScope lifetimeScope)
        {
            _lifetimeScope = lifetimeScope;
        }
    }

但这是一种服务定位器。我们隐藏了UowProductDataService个依赖关系。

所以我们应该实现一个提供者:

public IProductsManagerProvider : IProductsManager
{

}
public class ProductsManagerProvider : IProductsManagerProvider
{
    private readonly IUnitOfWork _uow;
    private readonly IProductsDataService _dataService;

    public ProductsManagerProvider (IUnitOfWork uow, IProductsDataService dataService)
    {
        _dataService = dataService;
        _uow = uow;
    }

    public bool AddProduct(ProductEntity product)
    {
        var result=false;
        var addedProduct = _dataService.AddProduct(product);
        if (addedProduct != null)
            result=_uow.Commit()>0;
        return result;
    }
}

我们将其注册为per dependency(因为我们会将其用于工厂)。

container.RegisterType<ProductsManagerProvider>().As<IProductsManagerProvider>().InstancePerDependency();

你的ProductsManager课应该是这样的。 (现在我们不隐藏任何家属)。

public class ProductsManager : IProductsManager
{
    private readonly Func<Owned<IProductsManagerProvider>> _managerProvider;
    //Now we don't hide any dependencies.
    public ProductsManager(Func<Owned<IProductsManagerProvider>> managerProvider)
    {
        _managerProvider = managerProvider;
    }

    public bool AddProduct(ProductEntity product)
    {
        using (var provider = _managerProvider())
        {
            return provider.Value.AddProduct(product);
        }
    }
}

我已经用我自己的课程进行了测试。

你有一个单独的经理实例,它有一个工厂来创建经理提供者。管理器提供程序是依赖的,因为每次我们都应该在单例中获取新实例。每个生命周期中提供程序中的所有内容,因此它们的生命周期是每个依赖生存期的连接提供程

当您在经理Container中添加产品时,会创建1 Provider,1 DbContext,1 DataService和1 UowDbContext已共享) 。在Provider中的方法返回后,Manager与所有已重新发布的实例(DbContex,Uow,DataService)一起(依赖于)。

答案 1 :(得分:2)

似乎问题并不是确保注入UnitOfWorkProductsRepository的DbContext实例是相同的。

这可以通过在解析InstancePerLifetimeScopeLifetimeScope之前将DbContext注册为IUnitOfWork并创建新的ProductsRepository来实现。 任何非您拥有的依赖关系将在处置LifetimeScope时被处置。

问题似乎是你在这两个班级之间没有明确的关系。您的UoW并不依赖于任何DbContext&#39;,它取决于当前事务中涉及的DbContext。那个特定的。

您的UoW和存储库之间也没有直接关系。这看起来不像UoW pattern

我无法弄清楚谁将展开IRepository创建的IRepositoryFactory。您正在使用容器来解决它(通过注入ILifetimeScope的{​​{1}})。除非从RepositoriesFactory获得该实例的任何人处置它,否则只能通过将Factory注入LifetimeScope来处理它。

可能出现的另一个问题是DbContext的所有权。您可以通过IRepositoryFactory上的Dispose将其置于using块上。但是您的IUnitOfWork也没有拥有该实例。容器呢。存储库是否也会尝试处理DbContext?他们还通过构造函数注入接收。

我建议重新考虑这个解决方案。