【问题标题】:How to detach entity from context when you no longer have a context reference当不再有上下文引用时如何从上下文中分离实体
【发布时间】:2014-12-06 19:03:42
【问题描述】:

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

然后我有类似以下的东西来检索我的数据:

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. 循环遍历所有结果,然后将它们添加到缓存中,并从当前上下文中Detaching,然后在从缓存中检索时将它们附加到新上下文中。

理想情况下,我希望能够从实体的一侧将上下文与实体分离,这样我就可以查看实体是否附加到当前上下文,如果没有,将其与旧上下文分离并附加它到新的。不过,从我所看到的情况来看,唯一可以分离它的方法是引用我不会拥有的旧上下文。

我是否遗漏了一些明显的东西?

编辑

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

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();

就目前而言,我正在强制加载未缓存的版本以解决此问题。

【问题讨论】:

  • 请显示引发此异常的代码。您在哪里将 Product 关联到另一个实体?

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


【解决方案1】:

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

所以请确保没有代理,而只是 POCO 被缓存,您可以通过在将对象提取到缓存的上下文中禁用代理创建来做到这一点:

db.Configuration.ProxyCreationEnabled = false;

(其中dbDbContext 实例)。

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

我想补充一点,如果您在实体模型中公开 OrderLine.ProductId,这一切都会变得容易得多:只需设置 Id 值就足够了。

【讨论】:

  • 谢谢。关于你的最后一句话,在这种情况下它很有意义(我会这样做),但我有另一个区域允许编辑产品,所以我会得到正在编辑的当前产品,更改值然后调用 @ 987654329@ 所以在这种情况下这无济于事。这个代理的东西听起来很有希望,所以我会看看(奇怪的是我以前没见过)。
【解决方案2】:

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

所以当你调用你的 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;
        }  
    }

【讨论】:

  • 感谢您的回复。从内存中,当我过去尝试过这个时,将检索到的缓存实体附加到我当前的上下文然后导致缓存的实体引用我当前的上下文,这意味着下次我从缓存中检索它时,它再次引用一个古老的背景。这听起来可能吗?可能是我误解了之前尝试此操作时遇到的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多