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.
我想了解为什么它不工作时调用本地。当相关的导航属性已经加载到上下文中时,为什么还要访问数据库来获取它们呢?还是我错过了什么?
嗯,我不知道为什么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);