[英]Entity Framework Code First - select based on type of child TPH class
我目前正在使用Entity Framework 4处理一个使用Table Per Hierarchy表示使用单个表的一组类的项目。 这种方式的工作方式是该表表示状态,不同的状态与不同的其他类相关联。
所以你可能会想象它看起来像这样,忽略了所有州共享的公共字段:
InactiveState
has a -> StateStore
ActiveState
has a -> ActivityLocation
CompletedState
has a -> CompletionDate
每个州都有一个属于它的所有InventoryItem的集合。
现在,我的库存中的每个项目都有许多状态,其中历史记录中的最后一个是当前状态。 要保存在列表中,我有一个快捷方式字段,指向我的库存的当前状态:
public class InventoryItem : Entity
{
// whole bunch of other properties
public virtual ICollection<InventoryState> StateHistory { get; set; }
public virtual InventoryState LastState { get; set; }
}
我遇到的第一个问题是,我想找到所有处于Active
状态的InventoryItems。
事实证明, Linq-To-Entities不支持GetType(),所以我不能使用像InventoryRepository.Get().Where( x => x.LastState.GetType() == someType )
这样的语句InventoryRepository.Get().Where( x => x.LastState.GetType() == someType )
。 我可以使用is
运算符,但这需要一个固定类型,而不是像这样的方法:
public ICollection<InventoryItem> GetInventoryItemsByState( Type state )
{
return inventoryRepository.Get().Where( x => x.LastState is state );
}
在进行Linq查询之前,我必须根据类型运行某种if语句,这感觉不对。 InventoryItem列表可能会变大,所以我需要在EF级别执行此操作,我无法将整个列表拉入内存并使用GetType()。
在这种情况下我有两个问题,关联得足够紧密,我认为它们可以合并,因为它们可能反映了我对自己缺乏了解:
Include
子表类型的相关项目,例如,如果我有一个InactiveState作为我的InventoryItem的子项,我可以为该InactiveState预加载StateStore? 是否可以使用Linq To Entities查找共享子表类型的项目列表?
我认为除了使用if / switch检查类型并使用is T
或OfType<T>
构建过滤器表达式之外,我认为不可能。 您可以将此逻辑封装到扩展方法中,例如,以便维护一个位置和可重用的方法:
public static class Extensions
{
public static IQueryable<InventoryItem> WhereIsLastState(
this IQueryable<InventoryItem> query, Type state)
{
if (state == typeof(InactiveState))
return query.Where(i => i.LastState is InactiveState);
if (state == typeof(ActiveState))
return query.Where(i => i.LastState is ActiveState);
if (state == typeof(CompletedState))
return query.Where(i => i.LastState is CompletedState);
throw new InvalidOperationException("Unsupported type...");
}
}
要像这样使用:
public ICollection<InventoryItem> GetInventoryItemsByState(Type state)
{
return inventoryRepository.Get().WhereIsLastState(state).ToList();
}
我不知道是否可以使用.NET Expression
API手动构建i => i.LastState is XXX
表达式,并基于传递给方法的Type
。 (老实说,我也会感兴趣,但我几乎没有关于表达操作的线索来回答这个问题。)
鉴于我没有使用延迟加载,是否可以使用TPH包含子表类型的相关项目,例如,如果我有一个InactiveState作为我的InventoryItem的子项,我可以为该InactiveState预加载StateStore?
我不确定我是否理解正确,但通常急于加载Include
不支持对特定包含的孩子进行任何过滤或附加操作。
避免这种限制并仍然在单个数据库往返中获得结果的一种方法是使用如下所示的投影:
var result = context.InventoryItems
.Select(i => new
{
InventoryItem = i,
LastState = i.LastState,
StateStore = (i.LastState is InactiveState)
? (i.LastState as InactiveState).StateStore
: null
})
.AsEnumerable()
.Select(x => x.InventoryItem)
.ToList();
如果查询是跟踪查询(在上面的示例中)并且关系不是多对多(它们不在您的示例中),则上下文将在实体加载到上下文时修复关系,是InventoryItem.LastState
和InventoryItem.LastState.StateStore
(如果LastState
的类型为InactiveState
)将被设置为已加载的实体( 就好像它们已经加载了急切加载)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.