C# Entity Framework每个HttpContext只使用一个ObjectContext

5
在使用Entity Framework 4的ASP.NET MVC 2中,我遇到了这个错误:“无法将实体对象引用多个IEntityChangeTracker实例”。在SO上的搜索显示,这可能是因为我有不同的Entity Framework ObjectContext实例,而每个HttpContext应该只有一个ObjectContext实例。我有这段代码(在我加入之前就已经写好),看起来正是这样 - 每个HttpContext都有一个ObjectContext。但我经常遇到“IEntityChangeTracker”异常,所以它可能没有按预期工作:
// in ObjectContextManager.cs
public const string ConnectionString = "name=MyAppEntities";
public const string ContainerName = "MyAppEntities";

public static ObjectContext GetObjectContext()
{
    ObjectContext objectContext = GetCurrentObjectContext();
    if (objectContext == null) // create and store the object context
    {   
        objectContext = new ObjectContext(ConnectionString, ContainerName);     
        objectContext.ContextOptions.LazyLoadingEnabled = true;    
        StoreCurrentObjectContext(objectContext);
    }
    return objectContext;
}

private static void StoreCurrentObjectContext(ObjectContext objectContext)
{
    if (HttpContext.Current.Items.Contains("EF.ObjectContext"))
        HttpContext.Current.Items["EF.ObjectContext"] = objectContext;
    else
        HttpContext.Current.Items.Add("EF.ObjectContext", objectContext);
}

private static ObjectContext GetCurrentObjectContext()
{
    ObjectContext objectContext = null;
    if (HttpContext.Current.Items.Contains("EF.ObjectContext")
        objectContext = (ObjectContext)HttpContext.Current.Items["EF.ObjectContext"];
    return objectContext;
}

我已经检查了这段代码,它看起来是正确的。据我所知,它为每个HttpContext返回一个ObjectContext实例。代码有问题吗?
如果代码没有问题,那么我为什么会得到“实体对象不能被IEntityChangeTracker的多个实例引用”的异常呢?
编辑:为了展示如何处理ObjectContext:
// in HttpRequestModule.cs
private void Application_EndRequest(object source, EventArgs e)
{
    ServiceLocator.Current.GetInstance<IRepositoryContext>().Terminate();
}

// in RepositoryContext.cs
public void Terminate() 
{
    ObjectContextManager.RemoveCurrentObjectContext();
}

// in ObjectContextManager.cs
public static void RemoveCurrentObjectContext()
{
    ObjectContext objectContext = GetCurrentObjectContext();
    if (objectContext != null)
    {
        HttpContext.Current.Items.Remove("EF.ObjectContext");
        objectContext.Dispose();
    }
}

你在 EndRequest 方法中释放了上下文吗? - Akhil
已更新以显示处理方法。 - JK.
1个回答

5

我猜您在某个地方存储了一个对象(最可能是使用进程模式的http缓存,但也可能是任何手动缓存,如共享字典),现在您以某种方式将该对象与其他内容关联起来,例如:

newOrder.OwnerUser = currentUser; // <== let's say currentUser came from cache
                                  // and newOrder was on your new entity context

因此,如果缓存的对象仍然认为它附加到上下文,则会出现问题;最重要的是,您可能会意外保持整个图形处于活动状态。
代码看起来没问题(只要在请求结束时将其处理掉),但这是一个很好的机会添加:
private const string EFContextKey = "EF.ObjectContext";

使用变量代替5个文字常量,可以避免一些风险;p


实际上,我已将一个对象存储在缓存中(Currency DefaultCurrency),然后将其附加到订单对象上(order.Currency = DefaultCurrency) - 这正是异常抛出的地方...这是一个非常有前途的线索。当我将DefaultCurrency保存到缓存时,它的类型为System.Data.Entity.DynamicProxies.Currency_F4008E27DE_etc,而不是POCO类Entities.Currency。我需要对这个对象做什么才能够安全地存储它在缓存中并稍后将其添加到另一个对象中,分离它? - JK.
1
@JK - 棘手的问题; 由于可能有多个线程同时尝试使用它,我建议最好编写一些代码来克隆它(创建一个不附加到上下文的普通 POCO),并存储一个克隆(避免保持图形活动的风险),每次取出时再克隆它。我不知道在 EF 中是否可能,但如果这是 L2S,我会设置 DefaultCurrencyId 而不是 DefaultCurrency - 避免一些问题情况。 - Marc Gravell
谢谢,我会看看我能做什么。 - JK.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接