如何在不再具有上下文引用时将实体从上下文中分离

本文关键字:上下文 实体 分离 引用 不再 | 更新日期: 2023-09-27 18:33:18

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

然后,我有如下所示的内容来检索我的数据:

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

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

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

编辑

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

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

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

如何在不再具有上下文引用时将实体从上下文中分离

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

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

db.Configuration.ProxyCreationEnabled = false;

(其中dbDbContext实例(。

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

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

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

因此,当您调用 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;
        }  
    }