在以下情况中,由于方法调用而嵌套了两个DbContexx:
public void Method_A() {
using (var db = new SomeDbContext()) {
//...do some work here
Method_B();
//...do some more work here
}
}
public void Method_B() {
using (var db = new SomeDbContext()) {
//...do some work
}
}
问题:
这种嵌套会导致任何问题吗? (并且会在正确的时间处理正确的DbContext吗?)
如果将Method_A重构为:
,这种嵌套是否被认为是不好的做法public void Method_A() {
using (var db = new SomeDbContext()) {
//...do some work here
}
Method_B();
using (var db = new SomeDbContext()) {
//...do some more work here
}
}
感谢。
答案 0 :(得分:39)
您的DbContext
派生类实际上至少为您管理了三件事:
实体框架通常会缓存元数据(第1项),以便所有上下文实例(或至少所有使用相同连接字符串的实例)共享它。所以这里没有理由让你担心。
正如其他评论中所提到的,您的代码会导致使用两个数据库连接。这对您来说可能是也可能不是问题。
您最终还有两个客户端缓存(第3项)。如果您碰巧从外部上下文加载实体,那么再次从内部上下文加载实体,您将在内存中有两个副本。这肯定会令人困惑,并可能导致微妙的错误。这意味着,如果您不想使用共享上下文对象,那么您的选项2可能会比选项1更好。
如果您使用交易,则需要进一步考虑。拥有多个数据库连接可能会导致事务被提升为分布式事务,这可能不是您想要的。既然你没有提到数据库事务,我不会在这里进一步讨论。
如果您使用此模式只是为了避免在代码中传递DbContext
个对象,那么您可能最好重构MethodB
以接收上下文作为参数。应该反复出现长期上下文对象的问题。根据经验,为单个数据库操作或一系列相关数据库操作创建新上下文。 (例如,请参阅this blog post和this question。)
(作为替代方案,您可以在接收现有连接的DbContext
派生类中添加构造函数。然后您可以在多个上下文之间共享相同的连接。)
一个有用的模式是编写自己的类,创建上下文对象并将其存储为私有字段或属性。然后,您使类实现IDisposable
,并且其Dispose()
方法处理上下文对象。您的呼叫代码会通知您班级的实例,而不必担心上下文或连接。
当您需要编写多线程代码时,这非常有用。数据库连接不是线程安全的,因此您必须一次只能从一个线程访问连接(因此也就是EF上下文)。如果限制太多,则需要多个连接(和上下文),每个线程一个。您可能会发现this很有趣。
答案 1 :(得分:1)
您可以通过传递给Method_B上下文来更改代码。如果这样做,则不需要创建第二个db调用SomeDbContext。
在此链接的stackoverflow中有一个问题答案 Proper use of "Using" statement for datacontext
答案 2 :(得分:1)
这是一个迟到的答案,但人们可能看起来这样是另一种方式。
创建类,关心为您处置。在某些情况下,会有一个可用于解决方案中不同位置的功能。这样就可以避免创建多个DbContext实例,并且可以根据需要使用嵌套调用。
粘贴简单的例子。
public class SomeContext : SomeDbContext
{
protected int UsingCount = 0;
public static SomeContext GetContext(SomeContext context)
{
if (context != null)
{
context.UsingCount++;
}
else
{
context = new SomeContext();
}
return context;
}
private SomeContext()
{
}
protected bool MyDisposing = true;
protected override void Dispose(bool disposing)
{
if (UsingCount == 0)
{
base.Dispose(MyDisposing);
MyDisposing = false;
}
else
{
UsingCount--;
}
}
public override int SaveChanges()
{
if (UsingCount == 0)
{
return base.SaveChanges();
}
else
{
return 0;
}
}
}
使用示例
public class ExmapleNesting
{
public void MethodA()
{
using (var context = SomeContext.GetContext(null))
{
// manipulate, save it, just do not call Dispose on context in using
MethodB(context);
}
MethodB();
}
public void MethodB(SomeContext someContext = null)
{
using (var context = SomeContext.GetContext(someContext))
{
// manipulate, save it, just do not call Dispose on context in using
// Even more nested functions if you'd like
}
}
}
简单易用。
答案 3 :(得分:0)
如果您认为与数据库的连接数量,以及必须打开新连接的次数的影响,这不是一个重要问题,并且您没有限制支持您的应用程序以最佳性能运行,一切正常。 你的代码效果很好。因为只创建db上下文对性能的影响很小,所以元数据将在首次加载后进行缓存,并且当代码需要执行查询时,就会发生与数据库的连接。通过liitle性能考虑和代码设计,我建议你让上下文工厂为每个应用程序实例提供每个Db Context的实例。
您可以查看此链接以获取更多性能注意事项 http://msdn.microsoft.com/en-us/data/hh949853