[英]EF - Best way to manage DbContext?
I know there are alot of articles regarding this topic, but wherever I looked, it was either too complicated or unclear to me. 我知道有很多关于该主题的文章,但是无论我去哪里看,对我来说要么太复杂,要么不清楚。
My team develops web application that uses Entity framework code first. 我的团队开发的Web应用程序首先使用Entity Framework代码。 We also use Autofac for dependency injection.
我们还使用Autofac进行依赖项注入。
Currently, the data access looks as follows: 当前,数据访问如下所示:
The API supplied to projects that do not contain EF: 提供给不包含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();
}
}
}
My DataServiceExecutor: 我的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;
}
}
}
All my repositories inherit from this abstract repository: 我所有的存储库都从该抽象存储库继承:
public abstract class EFRepository<T> : IRepository<T>
{
public void AddItem<T>(DbContext context, T item)
{
context.Set<T>().Add(item);
}
.
.
.
}
The good thing is that a context is used per transaction this way. 好处是,这样可以在每个事务中使用上下文。
The bad thing is that both my service methods and my repositories methods take context directly. 不好的是,我的服务方法和存储库方法都直接采用上下文。 The application is not so big, so for now the method injection of the context is fine, but it will probably become bigger and so the context injection at it's current state is problematic in my opinion.
应用程序不是那么大,所以现在上下文的方法注入是可以的,但是它可能会变得更大,因此我认为当前状态下的上下文注入是有问题的。 And it looks bad.
而且看起来很糟。
Maybe there are more pros and cons which I'm not aware of.. 也许还有更多我不了解的利弊。
Is there any way I'm not familiar with to make data access better? 有什么我不熟悉的方法可以改善数据访问?
Classes like DataServiceExecutor
(essentially a verb) always spell design flaws. 诸如
DataServiceExecutor
(本质上是动词)之类的类总是会拼写设计缺陷。 It's a method ( Execute...
) disguised as a class. 这是伪装为类的方法(
Execute...
)。 Responsibilities of such classes aren't clear because their functions inevitably belong to other classes. 这些类的职责尚不明确,因为它们的功能不可避免地属于其他类。
The problem with an in itself great pattern like Inversion of Control is that an IoC container can be used to inject any dependency. 像Inversion of Control这样的本身很棒的模式的问题在于,可以使用IoC容器注入任何依赖项。 So they allow you to create a tangle of dependencies and scattered responsibilities and still do a decent job managing life cycles.
因此,它们使您可以创建各种依赖关系和分散的职责,并且在管理生命周期方面仍然做得不错。 They may obscure life cycle problems you'd have otherwise.
它们可能会掩盖您原本会遇到的生命周期问题。
So let's ignore IoC for a moment and see what your code would look like with simple object creation if you call DataServiceExecutor.AddItem
. 因此,让我们暂时忽略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);
Inside dataServiceAddItem
method you essentially have: 在
dataServiceAddItem
方法内部,您基本上具有:
using (var context = _factory.Create())
{
executor.AddItem(context, product);
context.SaveChanges();
}
If DataService
would receive the productRepository
instead of the executor
this would boil down to: 如果
DataService
将接收productRepository
而不是executor
则可以归结为:
productRepository.AddItem(context, product);
context.SaveChanges();
The executor
can be taken out easily. executor
可以很容易地取出来。 Its only role seems to be error logging. 它的唯一作用似乎是错误记录。 But that could be done by
DataService
just as well. 但这也可以由
DataService
完成。
But we're not done yet. 但是我们还没有完成。 As you indicated yourself, these methods taking a context as parameter are a bit awkward.
正如您自己指出的那样,这些将上下文作为参数的方法有些尴尬。 But now
DataServiceExecutor
is out of the picture, it's far more natural to do: 但是现在
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();
}
And the DataService
is gone too. 而且
DataService
也消失了。
Now EFRepository
stores its context
as a member variable, and AddItem
looks like this: 现在,
EFRepository
将其context
存储为成员变量, AddItem
如下所示:
public void AddItem<T>(T item)
{
_context.Set<T>().Add(item);
}
And now back to IoC. 现在回到IoC。
In the light of IoC, the main problem of your code is this inside DataService.AddItem
: 根据IoC,您的代码的主要问题是在
DataService.AddItem
内部:
using (var context = _factory.Create())
You create a context that is not managed by the IoC container. 您创建一个不受IoC容器管理的上下文。 It's lifespan is scoped to the
AddItem
method. 它的寿命仅限于
AddItem
方法。 Therefore you have to pass it around all over the place to make sure everything within the business transaction uses this one instance. 因此,您必须将其传递到各处,以确保业务交易中的所有内容都使用该一个实例。 By bringing the repository's dependency (to a context) back to constructor injection, it's much easier to let the IoC container manage the context's lifespan.
通过将存储库的依赖关系(返回到上下文)返回到构造函数注入,让IoC容器管理上下文的生命周期变得容易得多。
By the way, you say that DataService
is part of "the API supplied to projects that do not contain EF". 顺便说一句,您说
DataService
是“提供给不包含EF的项目的API”的一部分。 But it does refer to MyContext
in its generic parameter. 但是它的确在其通用参数中引用了
MyContext
。 Maybe MyContext
is an abstraction too, I don't know. 也许
MyContext
也是一个抽象,我不知道。 Anyhow, you could supply instances of this abstraction by IoC as well. 无论如何,您也可以提供IoC的抽象实例。
My opion is that the solution is not correctly layered. 我的选择是解决方案未正确分层。 I guess the DataService is the top layer that is accessed from the outside world?
我猜DataService是从外部访问的顶层?
In that case i would change to the following: 在这种情况下,我将更改为以下内容:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.