【问题标题】:Referring to properties as substypes in queries when using TPH in EF Core在 EF Core 中使用 TPH 时将属性称为查询中的子类型
【发布时间】:2017-06-15 17:21:24
【问题描述】:

我有以下课程:

   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; }
   }

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

我现在想查询与特定区域直接关联或通过其父区域间接关联的所有“资产”。是否可以进行这样的查询?

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

    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 );

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

【问题讨论】:

  • 当您尝试运行查询时会发生什么?
  • 感谢您的评论。我进一步调查了这个问题。请参阅下面的答案。

标签: c# entity-framework-core


【解决方案1】:

事实证明,我并没有详尽地尝试过将它与“is”运算符结合使用的所有可能性(使用 cast/as)。

基本上以下两个示例会引发异常,指定属性“未定义”(System.ArgumentException: Property 'Int64 ParentAreaId' is not defined for type 'Area')

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 );

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多