Search code examples
inheritanceentity-framework-6table-per-hierarchy

Entity Framework: Query Multiple Types


I have a EF6 code-first model with inheritance, for example:

[Table("Things")]
abstract class AbstractThing
{
   public int ThingId { get; set; }
   public string Color { get; set; }
}

class Car : AbstractThing
{
   public string Brand { get; set; }
}

class Balloon : AbstractThing
{
   public double Price { get; set; }
}

class Animal : AbstractThing
{
   public int LegCount { get; set; }
}

I use table-per-hierarchy, so my "Things" table will have the columns:

  • ThingId : int
  • Color : string
  • Brand : string
  • Price : double
  • LegCount : int
  • Discriminator : string

Now, I want to query for things that are of specific types, but this list of types is dynamic. E.g. I want all red things that are Cars or Ballons. In SQL that is quite easily done, as I could just compile a where Discriminator in ('Car', 'Ballon') clause.

The problem here is that, to my knowledge, the only way to filter by type in EF Linq2SQL is by using the .OfType method (or use specific collections). But then I get back the filtered list that only contains that type, I cannot use an 'OR' or 'IN' clause. I can't really figure out how to do this without explicitly querying every set and making a union:

var queryable = Enumerable.Empty<Thing>().AsQueryable();

if (typesRequested.Contains(typeof(Car)))
{
   queryable = queryable.Union(context.Things.OfType<Car>());
}
if (typesRequested.Contains(typeof(Balloon)))
{
   queryable = queryable.Union(context.Things.OfType<Balloon>());
}
if (typesRequested.Contains(typeof(Animal)))
{
   queryable = queryable.Union(context.Things.OfType<Animal>());
}

As in my actual model I have a lot more inherited types, this approach is not so feasible. The workaround I have in place now is to have a custom 'type' column that is redundant with the 'Discriminator' column, but this is error prone and creates boiler plate code.

Is there any other way to dynamically restrict a queryable to return only a specific set of types?


Solution

  • This seems possible with EF Core: The 'Discriminator' property is now available as a Shadow Property. (https://learn.microsoft.com/en-us/ef/core/modeling/relational/inheritance#configuring-the-discriminator-property )

    i.e.:

    var types = typesRequested.Select(x => x.Name).ToArray();
    var filteredByType = context.Things.Where(tng => 
                     types.Contains(EF.Property<string>(tng, "Discriminator")));