4

我从外部源加载数据作为 xml,它被反序列化,然后我循环对象以将它们汇集到我的域实体中。

为了创建数据之间的关系并减少数据库调用,我编写了一个扩展方法来尝试从 DbSet.Local 中检索项目,如果它没有找到项目,那么它使用 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)

即使在此使用之前将订单和客户显式加载到上下文中,它也会为订单抛出空引用错误

context.Orders.Load()

但是,如果你这样做:

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

它会起作用的。

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

4

2 回答 2

2

好吧,我不确定为什么 DbSet.Local.xx 的行为与 DbSet.xx 不同,但我最终用作解决方案的只是简单地检查我的 lambda 中的 null :

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

这似乎可以防止我的扩展方法在首先检查本地项目时抛出错误,因此优雅地转移到调用数据存储。

于 2014-07-12T13:57:28.437 回答
0

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

当您从 a 中获取项目时DbSet<T>,Entity Framework 不会为您提供类型的对象T。相反,它创建了一个新类TProxy,该类overrides具有关联属性。这样,当你get的属性,Entity Framework 就会知道,并且可以拦截调用。

通过在实体框架之外反序列化您的对象,它没有机会将“钩子”添加到 getter,它需要延迟加载。因此,当您在对象的属性上调用 get 时,EF 不会延迟加载。

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

DbSet<T> set = ...;
T item = set.Create();
T deserialized = Deserialize(..);
AutoMapper.Map(deserialized, item);
set.Attach(item);
于 2014-07-10T18:26:24.637 回答