C# EF-管理DbContext的最佳方法?

C# EF-管理DbContext的最佳方法?,c#,entity-framework,ef-code-first,dbcontext,C#,Entity Framework,Ef Code First,Dbcontext,我知道有很多关于这个话题的文章,但无论我在哪里看,它要么太复杂,要么对我来说不清楚 我的团队首先开发使用实体框架代码的web应用程序。我们还使用Autofac进行依赖项注入 目前,数据访问如下所示: 提供给不包含EF的项目的API: public class DataService { private IDbContextFactory<MyContext> _factory; private IDataServiceExecutor _dataServiceExecut

我知道有很多关于这个话题的文章,但无论我在哪里看,它要么太复杂,要么对我来说不清楚

我的团队首先开发使用实体框架代码的web应用程序。我们还使用Autofac进行依赖项注入

目前,数据访问如下所示:

提供给不包含EF的项目的API:

public class DataService
{
   private IDbContextFactory<MyContext> _factory;
   private IDataServiceExecutor _dataServiceExecutor;

   public DataService(IDbContextFactory<MyContext> factory, IDataServiceExecutor executor)
   {
       _factory = factory;
       _dataServiceExecutor = executor;
   }

   public void AddItem(Product prod)
   {
       using (var context = _factory.Create())
       {
           if (_dataServiceExecutor.AddItem(context, prod))
               context.SaveChanges();
       }
   }
}
公共类数据服务
{
私营IDbContextFactory_工厂;
私有IDataServiceExecutor_数据服务执行器;
公共数据服务(IDbContextFactory工厂、IDataServiceExecutor执行器)
{
_工厂=工厂;
_dataServiceExecutor=执行器;
}
公共无效附加项(产品生产)
{
使用(var context=_factory.Create())
{
if(_dataServiceExecutor.AddItem(上下文,产品))
SaveChanges();
}
}
}
我的DataServiceExecutor:

public class DataServiceExecutor
{
    private IRepository<Product> _products;

    public DataService(IRepository<Product> products...more repositories)
    {
       _products = products;
    }

    public bool AddItem(DbContext context, Prouduct prod)
    {
        try
        {
            _products.AddItem(context, prod);

            return true;
        }
        catch(Exception ex)
        {
             Log(..)
             return false;
        }
    }
}
公共类DataServiceExecutor
{
私人IRepository产品;
公共数据服务(IRepository产品…更多存储库)
{
_产品=产品;
}
公共bool AddItem(DbContext上下文,product prod)
{
尝试
{
_产品。附加项(上下文、产品);
返回true;
}
捕获(例外情况除外)
{
日志(…)
返回false;
}
}
}
我的所有存储库都继承自此抽象存储库:

public abstract class EFRepository<T> : IRepository<T>
{
    public void AddItem<T>(DbContext context, T item)
    {
        context.Set<T>().Add(item);
    }

    .
    .
    .
}
公共抽象类eRepository:IRepository
{
公共void AddItem(DbContext上下文,T项)
{
context.Set().Add(项);
}
.
.
.
}
好的方面是,每个事务都以这种方式使用上下文

糟糕的是,我的服务方法和存储库方法都直接获取上下文。应用程序并没有那么大,所以目前上下文的方法注入还可以,但它可能会变得更大,因此在我看来,当前状态下的上下文注入是有问题的。看起来很糟糕

也许有更多的优点和缺点我不知道


有什么我不熟悉的方法可以让数据访问变得更好吗?

我的观点是解决方案没有正确分层。我猜数据服务是从外部世界访问的顶层

在这种情况下,我将改为:

  • 该服务通过构造函数注入存储库(或数据层)(然后正确地处理)。然后,服务方法只需要相关参数
  • 数据层的用途是什么?它能被移除吗?我通常在服务下面有一个数据层,保存所有存储库。在这种情况下,当服务处理数据层时,数据层可能负责处理所有存储库
  • 存储库可以保持原样,但应该通过构造函数注入上下文
DataServiceExecutor
(本质上是一个动词)这样的类总是拼写设计缺陷。它是一个伪装成类的方法(
Execute…
)。这些类的职责并不明确,因为它们的功能必然属于其他类

像控制反转这样的伟大模式本身的问题在于,可以使用IoC容器注入任何依赖项。因此,它们允许您创建一个复杂的依赖关系和分散的责任,并且仍然能够很好地管理生命周期。它们可能会掩盖你可能遇到的生命周期问题

因此,让我们暂时忽略IoC,看看如果调用
DataServiceExecutor.AddItem
,通过简单的对象创建,代码会是什么样子

var product = new Product();
var factory = new DbContextFactory<MyContext>(); // Dependencies unknown
var productRepository = new Repository<Product>(context);
var executor = new DataServiceExecutor(productRepository);
var dataService = new DataService(factory, executor);
如果
DataService
将接收
productRepository
而不是
executor
,这将归结为:

productRepository.AddItem(context, product);
context.SaveChanges();
执行器可以轻松取出。它唯一的作用似乎是错误日志记录。但这也可以通过
DataService
实现

但我们还没有完成。正如您自己指出的,这些将上下文作为参数的方法有点笨拙。但是现在
DataServiceExecutor
已经不存在了,这样做要自然得多:

var product = new Product();
var factory = new DbContextFactory<MyContext>();
using (var context = _factory.Create())
{
    var productRepository = new Repository<Product>(context);
    productRepository.AddItem(product);

    // And for example
    var orderRepository = new Repository<Order>(context);
    orderRepository.AddItem(order);

    // One SaveChanges call!
    context.SaveChanges();
}
现在回到国际奥委会。

根据IoC,您的代码的主要问题是在数据服务内部

using (var context = _factory.Create())
您创建的上下文不是由IoC容器管理的。它的寿命限定为
AddItem
方法。因此,您必须到处传递它,以确保业务事务中的所有内容都使用这个实例。通过将存储库的依赖项(对上下文)带回构造函数注入,让IoC容器管理上下文的生命周期就容易多了


顺便说一下,您说
DataService
是“提供给不包含EF的项目的API”的一部分。但它在其泛型参数中确实引用了
MyContext
。也许
MyContext
也是一种抽象,我不知道。无论如何,您也可以提供IoC的这种抽象的实例。

您可能会更幸运地在更新我的问题以使其更清楚的时候询问这个问题。如果你能用几段代码来描述你的答案,也许我会理解得更好谢谢你的详细答案!首先,该服务确实提供了API,Autofac在其ctor中注入了上下文工厂。其次,执行者的目的是实际执行操作,服务职责是在操作成功时保存更改和/或处置上下文。我知道传递上下文很难看,但我不知道如何使用依赖项注入来防止它,同时保持每个事务使用相同的上下文,我无法从您的回答中理解如何注入它。您可以以类似于注入工厂的方式来注入它。例如,通过构造函数注入。我相信有很多例子说明了如何使用Autofac注入上下文。但是它
public void AddItem<T>(T item)
{
    _context.Set<T>().Add(item);
}
using (var context = _factory.Create())