Woj*_*ski 5 c# concurrency entity-framework transactions ioc-container
在基于实体框架的应用程序的业务逻辑层中,所有作用于DB的方法应该(正如我所听到的)包含在:
using(FunkyContainer fc = new FunkyContainer())
{
// do the thing
fc.SaveChanges();
}
Run Code Online (Sandbox Code Playgroud)
当然,为了我自己的方便,这些方法经常使用彼此,为了不重复自己.我在这里看到的风险如下:
public void MainMethod()
{
using(FunkyContainer fc = new FunkyContainer())
{
// perform some operations on fc
// modify a few objects downloaded from DB
int x = HelperMethod();
// act on fc again
fc.SaveChanges();
}
}
public int HelperMethod()
{
using(FunkyContainer fc2 = new FunkyContainer())
{
// act on fc2 an then:
fc2.SaveChanges();
return 42;
}
}
Run Code Online (Sandbox Code Playgroud)
当容器fc2被创建时,我看起来并不好看,但fc仍处于打开状态且尚未保存.所以这引出了我的第一个问题:
我得出一个结论,我可以写一个简单的守护风格的对象,如下所示:
public sealed class FunkyContainerAccessGuard : IDisposable
{
private static FunkyContainer GlobalContainer { get; private set; }
public FunkyContainer Container // simply a non-static adapter for syntactic convenience
{
get
{
return GlobalContainer;
}
}
private bool IsRootOfHierarchy { get; set; }
public FunkyContainerAccessGuard()
{
IsRootOfHierarchy = (GlobalContainer == null);
if (IsRootOfHierarchy)
GlobalContainer = new FunkyContainer();
}
public void Dispose()
{
if (IsRootOfHierarchy)
{
GlobalContainer.Dispose();
GlobalContainer = null;
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在用法如下:
public void MainMethod()
{
using(FunkyContainerAccessGuard guard = new FunkyContainerAccessGuard())
{
FunkyContainer fc = guard.Container;
// do anything with fc
int x = HelperMethod();
fc.SaveChanges();
}
}
public int HelperMethod()
{
using(FunkyContainerAccessGuard guard = new FunkyContainerAccessGuard())
{
FunkyContainer fc2 = guard.Container;
// do anything with fc2
fc2.SaveChanges();
}
}
Run Code Online (Sandbox Code Playgroud)
当HelperMethod被调用时MainMethod,GlobalContainer已经创建了它,并且它被两种方法使用,所以没有冲突.此外,HelperMethod也可以单独使用,然后创建自己的容器.
然而,这对我来说似乎是一种巨大的矫枉过正; 所以:
谢谢.
一般来说,这是完全可以接受的,有时甚至是必要的,但你必须对此保持谨慎。在进行多线程操作时,同时拥有多个容器尤其方便。由于 db 通常的工作方式,每个线程都应该有自己的 DbContext,不应该与其他线程共享。同时使用多个DbContext的缺点是每个DbContext将使用单独的数据库连接,有时它们是有限的,这可能会导致应用程序偶尔无法连接到数据库。另一个缺点是一个 DbContext 生成的实体可能无法与其他 DbContext 生成的实体一起使用。在您的示例中,HelperMethod 返回原始类型,因此这是完全安全的,但如果它会返回 MainMethod 中的某个实体对象,您希望将其分配给 MainMethod DbContext 创建的实体的某些导航属性,那么您将收到异常。为了在 MainMethod 中克服这个问题,您必须使用 HelperMethod 返回的实体的 Id 来再次检索该实体,这次使用 fc 上下文。另一方面,使用多个上下文有一个优点 - 如果一个上下文有一些麻烦,例如它试图保存违反索引约束的内容,那么接下来所有保存更改的尝试都将导致与错误更改相同的异常。仍然待定。如果您使用多个 DbContext,那么如果其中一个失败,那么第二个将独立运行 - 这就是 DbContext 寿命不长的原因。所以一般来说我会说最好的使用规则是:
当然,如果要完成的工作很短,上述内容也适用。DbContext 不应该存在太久。最好的例子是 Web 应用程序 - 每个服务器请求都由单独的线程处理,并且生成响应的操作通常不会花费很长时间。在这种情况下,为了方便起见,为生成一个响应而执行的所有方法都应共享相同的 DbContext。但每个请求都应该由单独的 DbContext 提供服务。
您需要确保您的 DbContext 类是每个线程的单例,但每个线程都有自己的该类的实例。在我看来,确保这一点的最佳方法是通过 IoC。例如,在 Web 应用程序中的 Autofac 中,我使用以下规则注册 DbContext:
builder
.RegisterType<MyDbContext>()
.InstancePerHttpRequest();
Run Code Online (Sandbox Code Playgroud)
通过这种方式,autofac IoC 为每个请求生成一个 DbContext,并在请求服务线程内共享现有实例。您不需要在这里关心 DbContext 的处置。当你的线程结束时,你的 IoC 将会执行此操作。