EF6:包括导航属性在DbSet.Local

本文关键字:DbSet Local 属性 导航 包括 EF6 | 更新日期: 2023-09-27 18:12:04

我正在从外部源加载数据作为xml,它被反序列化,然后我循环遍历对象,将它们漏斗到我的域实体。

为了创建数据之间的关系并减少数据库调用,我编写了一个扩展方法来尝试从DbSet中检索项。本地优先,如果没有找到项目,然后使用DbSet.SingleOrDefault()查询数据库,如下所示。

public static TEntity SingleOrDefaultLocalFirst<TEntity>(this IDbSet<TEntity> set,
        Func<TEntity, bool> predicate) where TEntity : class
    {
        if (set == null)
            throw new ArgumentNullException("set");
        if (predicate == null)
            throw new ArgumentNullException("predicate");
        TEntity results = null;
        try
        {
            results = set.Local.SingleOrDefault(predicate);
        }
        catch (Exception e)
        {
            Debug.WriteLine(e.Message, "Error");
        }
        if (results != null)
        {
            return results;
        }
        return set.SingleOrDefault(predicate);
    }

try catch块是用来抑制我试图修复的问题的。

由于某些原因,查询本地存储时未填充导航属性。所以如果我用

(x=>x.Participant.Event.ExternalId==newItem.Id)

作为我的lambda, Participant的nav属性为空。

我觉得应该有一些方法来让这段代码不一致地生成空引用错误。

我已经尝试在循环开始使用

之前显式地从数据库加载参与者和事件数据
context.Participant.Load()

但这没有区别

有人能告诉我为什么导航属性是空的,以及如何填充他们最有效的方式?

并且,如果有人想知道为什么我不使用Find(),那是因为外部数据是在许多属性以及外部id字段上键入的,这不是我系统中的主键。

更新:

我不打算花时间来包含我的实际代码,因为有太多的代码需要精简成一个可用的示例,所以我将尝试使用Customer/Order/OrderItem的典型示例。

真正的问题是,当你有一个嵌套的实体,你尝试使用类似的东西来检查是否存在:

var orderLine = context.OrderLineItems.Local.SingleOrDefault(x=>x.Order.Number == 1234)

将抛出Order的nullreference错误,即使在此之前使用

显式地将Orders和Customers加载到上下文中
context.Orders.Load()

但是,如果你这样做:

var orderLine = context.OrderLineItems.SingleOrDefault(x=>x.Order.Number == 1234)

it will work.

我想了解为什么它不工作时调用本地。当相关的导航属性已经加载到上下文中时,为什么还要访问数据库来获取它们呢?还是我错过了什么?

EF6:包括导航属性在DbSet.Local

嗯,我不知道为什么DbSet. local .xx的行为不同于DbSet。xx比,但我最终使用的解决方案只是简单地检查null在我的lambda:

SingleOrDefaultLocalFirst(x=>
...
x.Participant!=null &&
x.Participant.Event.ExternalId==newItem.Id &&
...);

这似乎可以防止我的扩展方法在首先检查Local项时抛出错误,从而优雅地转向调用数据存储。

这里的问题是您"反序列化"的对象没有被代理。

当你从DbSet<T>中获得项目时,实体框架不会给你类型为T的对象。相反,它创建了一个新的类TProxy, overrides具有关联属性。这样,当你get属性,实体框架将知道,并可以拦截调用。

通过在实体框架之外反序列化你的对象,它没有机会向getter添加"钩子",它需要惰性加载。所以当你在对象的属性上调用get时,EF不会延迟加载。

解决方案是使用实体框架实例化所有T对象,然后反序列化到这些对象,然后附加它们。

DbSet<T> set = ...;
T item = set.Create();
T deserialized = Deserialize(..);
AutoMapper.Map(deserialized, item);
set.Attach(item);