简体   繁体   English

EF6:在DbSet.Local中包含导航属性

[英]EF6: Include Navigation Properties in DbSet.Local

I am loading data from an external source as xml, it gets deserialized and then I loop over the object to funnel them into my domain entities. 我正在从外部源加载数据作为xml,它被反序列化然后我循环对象以将它们汇集到我的域实体中。

In order to create the relationships between data and cut down on database calls I wrote an extension method to attempt to retrieve items from DbSet.Local first if it doesnt find the item then it uses DbSet.SingleOrDefault() to query the database as shown below. 为了创建数据之间的关系并减少数据库调用,我编写了一个扩展方法,尝试从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);
    }

The try catch block is there to suppress the problem I am trying to fix. try catch块可以解决我想要解决的问题。

For some reason the navigation properties when querying the local store are not populated. 由于某种原因,未填充查询本地存储时的导航属性。 So if I use something like 所以,如果我使用类似的东西

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

as my lambda, the Participant nav property is null. 作为我的lambda,Participant nav属性为null。

I feel like there should be some way to get this code to not consistently generate null reference errors. 我觉得应该有一些方法让这段代码不能始终如一地生成空引用错误。

I have tried explicitly loading the Participant and Event data from the database before my loop starts using 我已经尝试在循环开始使用之前从数据库中显式加载Participant和Event数据

context.Participant.Load()

but this makes no difference. 但这没什么区别。

Can someone tell me why the navigation properties are null and how to populate them the most efficient way? 有人能告诉我为什么导航属性为null以及如何以最有效的方式填充它们?

And, if anyone is wondering why I am not using Find(), it is because the external data is keyed on a number of properties as well as an external id field which is not the primary key in my system. 而且,如果有人想知道我为什么不使用Find(),那是因为外部数据被键入了许多属性以及外部id字段,而不是我系统中的主键。

Update: 更新:

Im not going to take the time to include my real code because there is just too much to whittle down to a usable example so I will try and use the Customer/Order/OrderItem typical example. 我不打算花时间来包含我的真实代码,因为有太多的东西要削减到一个可用的例子,所以我将尝试使用Customer / Order / OrderItem典型示例。

The real issue is that when you have a nested entity and you try and check for existence using something like: 真正的问题是,当你有一个嵌套的实体,并尝试使用类似的东西检查存在:

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

it will throw a nullreference error for Order even if the Orders and Customers are loaded explicitly into the context prior to this using 即使Orders和Customers在使用之前明确加载到上下文中,它也会为Order抛出一个null引用错误

context.Orders.Load()

But, if you do this: 但是,如果你这样做:

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

it will work. 它会工作。

I want to understand why it doesnt work when calling Local. 我想了解为什么它在调用Local时不起作用。 Why should we have to make a trip to the database to get the related nav properties when they already have been loaded into the context? 为什么我们必须访问数据库以获取已经加载到上下文中的相关导航属性? or am I missing something? 还是我错过了什么?

Well, I'm not sure why the behavior of DbSet.Local.xx is different than DbSet.xx than but what I eventually used as a solution was just to simply check for null in my lambda: 好吧,我不确定为什么DbSet.Local.xx的行为与DbSet.xx不同,但我最终用作解决方案只是在我的lambda中检查null:

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

This seems to prevent my extension method from throwing an error when checking the Local items first and so gracefully moves onto calling the data store. 这似乎阻止了我的扩展方法在首先检查Local项时抛出错误,因此优雅地移动到调用数据存储。

The problem here is that the object you "deserialized" aren't Proxied. 这里的问题是你“反序列化”的对象不是Proxied。

When you get items out of a DbSet<T> , Entity Framework does not give you objects of type T . 当您从DbSet<T>获取项目时,实体框架不会为您提供类型为T对象。 Instead it creates a new class TProxy , which overrides the association properties. 相反,它会创建一个新类TProxy ,它overrides关联属性。 That way, when you get the property, Entity Framework will know, and can intercept the call. 这样,当你get属性时,Entity Framework就会知道,并且可以拦截调用。

By deserializing your objects outside of Entity Framework, it does not have a chance to add the "hooks" to the getter, that it needs to Lazy load. 通过在Entity Framework之外反序列化对象,它没有机会将“钩子”添加到getter,它需要延迟加载。 So when you call get on the object's properties, EF does not lazy load. 因此,当您调用get对象的属性时,EF不会延迟加载。

The solution to this is to use Entity Framework to instanciate ALL your T objects, then deserialize into these objects, then attach them. 解决方案是使用Entity Framework实例化所有T对象,然后反序列化为这些对象,然后附加它们。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM