Include for derived types in Entity Framework 2.1

When I wrote user interface for my TemperatureStation solution I faced some bad problems with Entity Framework Core when trying to query base type and get some navigation properties of derived types included. I was able to come out with some work-arounds that were far from being satisfying for me. Include for derived types in Entity Framework Core solves the problem.

Problem

Suppose we have some Entity Framework Core model classes representing readings taken from sensors. There are also readings calculated by special calculator classes. This is simplified fragment of code from my TemperatureStation solution available in GitHub. In some views I need to load readings and include calculators and sensors to display their names with readings. Take a look at the following code.

public abstract class Reading
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public DateTime ReadingTime { get; set; }

    public string ReadingType { get; set; }

    [NotMapped]
    public abstract string Name { get; }

    public double Value { get; set; }
}

public class Calculator
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

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

public class CalculatorReading : Reading
{
    [Required]
    public Calculator Calculator { get; set; }

    public override string Name
    {
        get { return Calculator?.Name; }
    }
}

public class Sensor
{
    [Key]
    public string Id { get; set; }

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

public class SensorReading : Reading
{
    [Required]
    public Sensor Sensor { get; set; }

    public override string Name
    {
        get { return Sensor?.Name; }
    }
}

The problem is that we don’t have good way to load Sensor and Calculator with readings of their type. Okay, it’s possible to load all sensors and calculators to memory before querying and having change tracking turned on event for read-only cases but it is hack that works only in specific situations.

Including properties of derived types

Entity Framework 2.1 solves the issue by introducing include for derived types. We can cast base type to derived type when calling Include() method and tell what navigation property we want to be included like shown next.

var readings = _context.Readings
                        .Include(reading => (reading as SensorReading).Sensor)
                        .Include(reading => (reading as CalculatorReading).Calculator)
                        .OrderByDescending(reading => reading.ReadingTime)
                        .Take(10)
                        .ToList();

This is completely valid query in Entity Framework Core 2.1 and it helps to avoid all kind of nasty hacks we needed with limitations we had before.

Wrapping up

Entity Framework 2.1 is big step forward and I am glad to see that painful problems get solved version by version. Include for derived types was one of those problems that wasn’t easy to solve before. It needed work-arounds and sometimes pretty nasty hacks to make things work like expected. Include for derived types is elegant and nice solution to those problems.

Gunnar Peipman

Gunnar Peipman is ASP.NET, Azure and SharePoint fan, Estonian Microsoft user group leader, blogger, conference speaker, teacher, and tech maniac. Since 2008 he is Microsoft MVP specialized on ASP.NET.

    6 thoughts on “Include for derived types in Entity Framework 2.1

    • Pingback:The Morning Brew - Chris Alcock » The Morning Brew #2599

    • October 9, 2019 at 6:39 pm
      Permalink

      Please use fixed-width font for code snippets. It is a convention and much more readable.

    • October 9, 2019 at 8:52 pm
      Permalink

      The font here is what Visual Studio is giving me when selecting Copy as HTML. It’s Consolas.

      Anyway thanks for suggestion. I will try some fixed fonts and maybe I will find something that fits with blog design.

    • January 29, 2020 at 9:19 am
      Permalink

      No success on implementing this on efcore 3.1.
      i was hoping that u could help.

      Currently my code look like :

      context.Sales.Include(x => x.SaleDetails)
      .ThenInclude(x => (x as ProductSaleDetail).Product)
      .Include(x => x.SaleDetails)
      .ThenInclude(x => (x as ProductSaleDetail).Unit)
      .Include(x => x.SaleDetails)
      .ThenInclude(x => (x as ServiceSaleDetail).Service);

    • January 29, 2020 at 10:51 am
      Permalink

      What is the problem you face with this code? Any errors? Properties are left empty?

    • March 22, 2020 at 6:43 pm
      Permalink

      Hi, do you know how can I resolve the same problem using Entity Framework 6? It would be great!
      Thanks

    Leave a Reply

    Your email address will not be published. Required fields are marked *