当您不再具有上下文引用时,如何从上下文中分离实体

时间:2014-12-06 19:03:43

标签: c# entity-framework caching entity-framework-6 datacontext

我正在我的网站上预加载产品数据,因为我需要快速访问它,并且宁愿在应用程序启动时加载一次,而不是仅在它的请求时加载。这是确保事物快速加载的一种方法。我这样做是通过将我的产品实体与其他实体一起加载并将它们存储在内存中来实现的。

然后我有类似以下内容来检索我的数据:

public override IEnumerable<Product> Get()
{
    var result = _cacheManager.Get(PreLoadCacheKey) as IEnumerable<Product>;
    if (result != null)
        return result;

    return base.Get();
}

我面临的问题是,在我使用依赖注入时,我不控制数据上下文的范围(除了将其设置为InstancePerRequest()并保留它),所以当我缓存我的实体时,他们仍然有对原始背景的引用。

我尝试过各种各样的东西,但似乎无法解决这个问题:

 The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects. 

尝试将从缓存中检索到的实体附加到我要添加到数据库的新实体时。

我尝试过的事情:

  1. 使用AsNoTracking()
  2. 检索数据
  3. 在将所有结果添加到缓存之前循环遍历所有结果,并从当前上下文中Detach将它们附加到从缓存中检索的新上下文中。
  4. 理想情况下,我希望能够从实体一侧分离实体的上下文,以便我可以看到实体是否附加到当前上下文,如果没有,则将其从旧实体上分离并附加它到了新的。从我所看到的情况来看,你唯一可以分离它的方法是引用我不会拥有的旧语境。

    我错过了一些明显的东西吗?

    修改

    这是我将实体附加到另一个上下文的一个示例:

    var orderLine = order.Lines.FirstOrDefault(ol => ol.Product.Id == product.Id) ?? new SalesOrderLine();
    
    orderLine.Price = price.Price;
    orderLine.Product = product; //This is my cache'd entity.
    orderLine.ProductPriceType = price.ProductPriceType;
    orderLine.Quantity += qty;
    
    order.Customer = customer;
    order.Lines.Add(orderLine);
    order.Status = SalesOrderStatus.Current;
    
    Update(order);
    SaveChanges();
    

    目前,我正在强行加载未缓存版本以解决此问题。

2 个答案:

答案 0 :(得分:1)

使用AsNoTracking检索缓存数据是第一步。但(显然)缓存中的对象被创建为代理,因此他们知道已经被另一个上下文创建。

因此,请确保没有代理,但只有POCO被缓存,您可以通过在将对象提取到缓存的上下文中禁用代理创建来执行此操作:

db.Configuration.ProxyCreationEnabled = false;

(其中dbDbContext个实例)。

然后确保在将对象用于新上下文之前将其附加到新上下文中。这很重要,因为如果您Add()新的OrderLine,EF也可能会尝试添加新的Product

我想补充一点,如果您在实体模型中公开OrderLine.ProductId,这将会更容易:只需设置Id值即可。

答案 1 :(得分:0)

由于您无法控制上下文对象的生命周期,因此我认为在加载时始终分离您的产品对象并在保存它们时附加它们是有意义的。

所以当你调用Get()方法而不只是检查缓存管理器,如果没有找到从base.get()返回时,请执行以下操作

    public override IEnumerable<Product> Get()
    {
        var result = _cacheManager.Get(PreLoadCacheKey) as IEnumerable<Product>;
        if (result != null)
            return result;


        return DetachAndReturn();
    }

    private IEnumerable<Product> DetachAndReturn()
    {
        foreach (var product in base.Get())
        {
            base.Detach(product);
            yield return product;
        }  
    }