简体   繁体   中英

Referring to properties as substypes in queries when using TPH in EF Core

I have the following classes:

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

Basically, I have an asset, which is associated to an area. And there are different types of areas that can fit into a hierarchy.

I would now like to query all 'Assets' that are associated either directly with a specific area, or indirectly through it's parent. Is it possible to make such a query?

I feel like something like this, should be possible:

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

But it is not. Is it possible to do something like this?

As it turns out, I had not exhausitively tried every possibility of casting it (using cast/as) in combination with the "is" operator.

Basically the following two examples throws an exception specifying that the property is "not defined" (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 );

and

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

And the next example throws a NullReferenceException (presumably because it actually uses the predicate to apply an additional filter after turning the expression into a sql query). Hence, if the area is not of the correct type, it will be null and therefore throw a NullReferenceException. (This is the example I posted in the original question)

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

However, the following actually works and it was basically the only combination I had not tried prior to posting this question (using both the both operators: 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 );

Basically, this means you should always use the "is/as" operators in your linq queries to EF Core (atleast for the SQL Server provider).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM