在基于实体框架的应用程序的业务逻辑层中,所有作用于DB的方法应该(如我所知)包含在:
中using(FunkyContainer fc = new FunkyContainer())
{
// do the thing
fc.SaveChanges();
}
当然,为了我自己的方便,这些方法常常互相使用,为了不重复自己。我在这里看到的风险如下:
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;
}
}
创建容器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;
}
}
}
现在用法如下:
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();
}
}
当HelperMethod
调用MainMethod
时,GlobalContainer
已经创建,并且两种方法都使用它,因此没有冲突。此外,HelperMethod
也可以单独使用,然后创建自己的容器。
然而,这对我来说似乎是一种巨大的矫枉过正;这样:
谢谢。
答案 0 :(得分:3)
一般来说,这是完全可以接受的,有时甚至是必要的,但你必须要谨慎。在进行多线程操作时,同时拥有多个容器尤其方便。由于db的工作原理通常每个线程都应该有自己的DbContext,不应该与其他线程共享。在同一时间使用多个DbContext的缺点是它们中的每一个都将使用单独的数据库连接,有时它们是有限的,可能导致应用程序偶尔无法连接到数据库。另一个缺点是由一个DbContext生成的实体可能不会与其他DbContext生成的实体一起使用。在您的示例中,HelperMethod返回基本类型,因此这是非常安全的,但是如果它返回一些实体对象,您希望在MainMethod中为实例分配由MainMethod DbContext创建的实体的某些导航属性,那么您将收到一个异常。要在MainMethod中克服这个问题,您必须使用HelperMethod返回的实体Id来再次检索该实体,这次使用fc上下文。另一方面,使用多个上下文有一个优点 - 如果一个上下文有一些麻烦,例如它试图保存违反索引constaint的东西,那么保存更改的所有下一次尝试将导致相同的异常,因为错误的更改将还在等待。如果你使用多个DbContexts然后如果一个会失败,那么第二个将独立运行 - 这就是为什么DbContexts不应该长寿。所以一般来说我会说最好的使用规则是:
当然,如果要完成的工作很短,则上述情况适用。 DbContext不应该长寿。最好的例子是Web应用程序 - 每个服务器请求由单独的线程处理,生成响应的操作通常不需要很长时间。在这种情况下,为了方便起见,所有为生成一个响应而执行的方法都应该共享相同的DbContext。但是每个请求都应该由单独的DbContext提供。
您需要确保的是,您的DbContext类是每个线程的单例,但每个线程都有自己的该类实例。在我看来,确保这一点的最佳方法是使用IoC。例如,在Web应用程序的Autofac中,我使用以下规则注册我的DbContext:
builder
.RegisterType<MyDbContext>()
.InstancePerHttpRequest();
这样autofac IoC就每个请求生成一个DbContext,并在请求服务线程内共享现有实例。你不需要在这里处理你的DbContext。当您的线程结束时,您的IoC将执行此操作。
答案 1 :(得分:2)
在大多数情况下,同时处理多个连接并不是正确的方法,因为:
这些是非常严重的缺点。通常,最好的模型是为应用程序正在处理的请求(HTTP或WCF请求)提供一个上下文,连接和事务。设置非常简单,避免了很多问题。
EF应该用作实时对象模型。不要将它减少到CRUD来削弱它。
static FunkyContainer GlobalContainer
这不起作用。您不应该跨请求共享上下文。超级危险。考虑在HttpContext.Items
或您应用中的每个请求存储中存储上下文。