EF - 管理 DbContext 的最佳方式?
EF - Best way to manage DbContext?
我知道有很多关于这个主题的文章,但无论我看哪里,对我来说不是太复杂就是不清楚。
我的团队首先开发使用 Entity framework 代码的 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();
}
}
}
我的数据服务执行器:
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;
}
}
}
我所有的存储库都继承自这个抽象存储库:
public abstract class EFRepository<T> : IRepository<T>
{
public void AddItem<T>(DbContext context, T item)
{
context.Set<T>().Add(item);
}
.
.
.
}
好处是这样每个事务都会使用一个上下文。
不好的是,我的服务方法和存储库方法都直接采用上下文。应用程序不是那么大,所以现在上下文的方法注入很好,但它可能会变得更大,所以在我看来,当前状态下的上下文注入是有问题的。而且看起来很糟糕。
也许还有更多我不知道的优点和缺点..
有什么我不熟悉的方法可以更好地访问数据吗?
我的意见是解决方案没有正确分层。我猜DataService是从外界访问的顶层?
在那种情况下,我将更改为以下内容:
- 服务获取通过构造函数注入的存储库(或数据层)(然后正确处置)。然后服务方法只需要相关参数。
- 数据层是做什么用的?可以删除吗?我通常在包含所有存储库的服务下方有一个数据层。在这种情况下,数据层可以在服务处理数据层时负责处理所有存储库。
- 存储库可以保持原样,但应该通过构造函数注入上下文。
类 就像 DataServiceExecutor
(本质上是一个动词)总是拼写设计缺陷。这是一种伪装成 class 的方法 (Execute...
)。此类 classes 的职责不明确,因为它们的功能不可避免地属于其他 classes。
像控制反转这样的伟大模式本身存在的问题是 IoC 容器可用于注入 any 依赖项。因此,它们允许您创建混乱的依赖关系和分散的职责,并且仍然可以很好地管理生命周期。它们可能会掩盖您本来会遇到的生命周期问题。
所以让我们暂时忽略 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);
在 dataServiceAddItem
方法中你基本上有:
using (var context = _factory.Create())
{
executor.AddItem(context, product);
context.SaveChanges();
}
如果 DataService
会收到 productRepository
而不是 executor
这将归结为:
productRepository.AddItem(context, product);
context.SaveChanges();
executor
可以轻松取出。它的唯一作用似乎是错误记录。但这也可以由 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();
}
DataService
也不见了。
现在EFRepository
将它的context
存储为一个成员变量,AddItem
看起来像这样:
public void AddItem<T>(T item)
{
_context.Set<T>().Add(item);
}
现在回到 IoC。
根据IoC,你代码的主要问题是这个DataService.AddItem
:
using (var context = _factory.Create())
您创建了一个不受 IoC 容器管理的上下文。它的生命周期在 AddItem
方法范围内。因此,您必须将它到处传递,以确保业务交易中的所有内容都使用这个实例。通过将存储库的依赖性(对上下文)带回构造函数注入,让 IoC 容器管理上下文的生命周期变得容易得多。
对了,你说DataService
是"the API supplied to projects that do not contain EF"的一部分。但它确实在其通用参数中引用了 MyContext
。也许 MyContext
也是一种抽象,我不知道。无论如何,您也可以通过 IoC 提供这种抽象的实例。
我知道有很多关于这个主题的文章,但无论我看哪里,对我来说不是太复杂就是不清楚。
我的团队首先开发使用 Entity framework 代码的 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();
}
}
}
我的数据服务执行器:
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;
}
}
}
我所有的存储库都继承自这个抽象存储库:
public abstract class EFRepository<T> : IRepository<T>
{
public void AddItem<T>(DbContext context, T item)
{
context.Set<T>().Add(item);
}
.
.
.
}
好处是这样每个事务都会使用一个上下文。
不好的是,我的服务方法和存储库方法都直接采用上下文。应用程序不是那么大,所以现在上下文的方法注入很好,但它可能会变得更大,所以在我看来,当前状态下的上下文注入是有问题的。而且看起来很糟糕。
也许还有更多我不知道的优点和缺点..
有什么我不熟悉的方法可以更好地访问数据吗?
我的意见是解决方案没有正确分层。我猜DataService是从外界访问的顶层?
在那种情况下,我将更改为以下内容:
- 服务获取通过构造函数注入的存储库(或数据层)(然后正确处置)。然后服务方法只需要相关参数。
- 数据层是做什么用的?可以删除吗?我通常在包含所有存储库的服务下方有一个数据层。在这种情况下,数据层可以在服务处理数据层时负责处理所有存储库。
- 存储库可以保持原样,但应该通过构造函数注入上下文。
类 就像 DataServiceExecutor
(本质上是一个动词)总是拼写设计缺陷。这是一种伪装成 class 的方法 (Execute...
)。此类 classes 的职责不明确,因为它们的功能不可避免地属于其他 classes。
像控制反转这样的伟大模式本身存在的问题是 IoC 容器可用于注入 any 依赖项。因此,它们允许您创建混乱的依赖关系和分散的职责,并且仍然可以很好地管理生命周期。它们可能会掩盖您本来会遇到的生命周期问题。
所以让我们暂时忽略 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);
在 dataServiceAddItem
方法中你基本上有:
using (var context = _factory.Create())
{
executor.AddItem(context, product);
context.SaveChanges();
}
如果 DataService
会收到 productRepository
而不是 executor
这将归结为:
productRepository.AddItem(context, product);
context.SaveChanges();
executor
可以轻松取出。它的唯一作用似乎是错误记录。但这也可以由 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();
}
DataService
也不见了。
现在EFRepository
将它的context
存储为一个成员变量,AddItem
看起来像这样:
public void AddItem<T>(T item)
{
_context.Set<T>().Add(item);
}
现在回到 IoC。
根据IoC,你代码的主要问题是这个DataService.AddItem
:
using (var context = _factory.Create())
您创建了一个不受 IoC 容器管理的上下文。它的生命周期在 AddItem
方法范围内。因此,您必须将它到处传递,以确保业务交易中的所有内容都使用这个实例。通过将存储库的依赖性(对上下文)带回构造函数注入,让 IoC 容器管理上下文的生命周期变得容易得多。
对了,你说DataService
是"the API supplied to projects that do not contain EF"的一部分。但它确实在其通用参数中引用了 MyContext
。也许 MyContext
也是一种抽象,我不知道。无论如何,您也可以通过 IoC 提供这种抽象的实例。