【问题标题】:Entity Framework Code First - select based on type of child TPH classEntity Framework Code First - 根据子 TPH 类的类型进行选择
【发布时间】:2013-04-18 16:22:16
【问题描述】:

我目前正在使用 Entity Framework 4 处理一个项目,该项目使用 Table Per Hierarchy 来表示使用单个表的一组类。其工作方式是该表表示状态,并且不同的状态与不同的其他类相关联。

所以你可能会想象它看起来像这样,忽略所有州共享的公共字段:

InactiveState 
  has a -> StateStore

ActiveState 
  has a -> ActivityLocation

CompletedState
  has a -> CompletionDate

每个状态都有属于它的所有 InventoryItems 的集合。

现在我的库存中的每个项目都有许多状态,历史中的最后一个是当前状态。为了保存在列表中,我有一个指向我的库存当前状态的快捷方式字段:

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 doesn't support GetType(),所以我不能使用像InventoryRepository.Get().Where( x =&gt; 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()。

在这种情况下,我有两个问题,它们之间的联系足够紧密,我认为它们可以结合起来,因为它们可能反映了我缺乏理解:

  • 是否可以使用 Linq To Entities 找到共享子表类型的项目列表?
  • 鉴于我没有使用延迟加载,是否可以使用 TPH 为子表类型 Include 相关项,例如,如果我有一个 InactiveState 作为我的 InventoryItem 的子项,我可以预加载 StateStore那个 InactiveState?

【问题讨论】:

    标签: c# entity-framework linq-to-entities table-per-hierarchy


    【解决方案1】:

    是否可以找到共享子表类型的项目列表 使用 Linq To 实体?

    我认为除了使用 if/switch 来检查类型并使用 is TOfType&lt;T&gt; 构建过滤器表达式之外,不可能有其他方式。您可以将此逻辑封装到一个扩展方法中,例如拥有一个可维护的地方和一个可重用的方法:

    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 并基于传递给方法的 Type 手动构建 i =&gt; i.LastState is XXX 表达式。 (老实说,我也会感兴趣,但我几乎不知道如何自己回答这个问题。)

    鉴于我没有使用延迟加载,是否可以包含 使用 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.LastStateInventoryItem.LastState.StateStore(如果LastStateInactiveState 类型)将被设置为加载的实体(就好像它们已经被预先加载)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-06-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-31
      • 2012-08-09
      • 1970-01-01
      相关资源
      最近更新 更多