在EF Core中使用TPH时,将属性称为查询中的子类型

时间:2017-06-14 13:10:17

标签: c# entity-framework-core

我有以下课程:

   public abstract class Area
   {
      public long Id { get; set; }

      [Required]
      public string Name { get; set; }

      public ICollection<Asset> Assets { get; set; }
   }

   public class AreaWithParent : Area
   {
      public AreaAsParent ParentArea { get; set; }

      public long ParentAreaId { get; set; }
   }

   public class AreaAsParent : Area
   {
      public ICollection<AreaWithParent> AreasWithParent { get; set; }
   }

   public class Asset
   {
      public long Id { get; set; }

      public long? AreaId { get; set; }

      public Area Area { get; set; }
   }

基本上,我有一个资产,它与一个区域相关联。并且有不同类型的区域可以适合层次结构。

我现在想查询所有资产&#39;直接与特定区域相关联,或通过其父母间接相关联。是否可以进行此类查询?

我觉得这样的事情应该是可能的:

    var areaId = /* the area id I want to query for */

    var assets = await ctx.Assets
       .Where( x => x.AreaId == areaId || ( x.Area as AreaWithParent ).ParentAreaId == areaId )
       .ToListAsync( cancellationToken );

但事实并非如此。有可能做这样的事吗?

1 个答案:

答案 0 :(得分:2)

事实证明,我并没有彻底尝试过将它(使用cast / as)和“is”运算符组合在一起的所有可能性。

基本上,以下两个示例抛出一个异常,指定该属性为“未定义”(System.ArgumentException:未为类型“Area”定义属性'Int64 ParentAreaId')

var areaId = /* the area id I want to query for */

var assets = await ctx.Assets
   .Where( x => x.AreaId == areaId || ( (AreaWithParent)x.Area ).ParentAreaId == areaId )
   .ToListAsync( cancellationToken );

var areaId = /* the area id I want to query for */

var assets = await ctx.Assets
   .Where( x => x.AreaId == areaId || ( x.Area is AreaWithParent && ( (AreaWithParent)x.Area ).ParentAreaId == areaId ) )
   .ToListAsync( cancellationToken );

下一个示例抛出NullReferenceException(可能是因为它实际上使用谓词在将表达式转换为sql查询后应用其他过滤器)。因此,如果该区域的类型不正确,则它将为null,因此抛出NullReferenceException。 (这是我在原始问题中发布的示例)

var areaId = /* the area id I want to query for */

var assets = await ctx.Assets
   .Where( x => x.AreaId == areaId || ( x.Area as AreaWithParent ).ParentAreaId == areaId )
   .ToListAsync( cancellationToken );

然而,以下实际上是有效的,它基本上是我在发布此问题之前没有尝试过的唯一组合(使用两个运算符:is / as):

var areaId = /* the area id I want to query for */

var assets = await ctx.Assets
   .Where( x => x.AreaId == areaId || ( x.Area is AreaWithParent && ( x.Area as AreaWithParent ).ParentAreaId == areaId ) )
   .ToListAsync( cancellationToken );

基本上,这意味着您应该始终在linq查询中使用“is / as”运算符到EF Core(至少对于SQL Server提供程序)。