Arad
Arad

Reputation: 12814

How to avoid repeating property projections when using EF Core inheritance?

I'm struggling with how to avoid repeating projection logic when using inheritance in EF Core.

Here's my scenario: I have three types:

  1. Lesson (which is an abstract class) (Properties: Id, Title, etc.)
  2. ArticleLesson (which inherits from Lesson) (Properties: Content, TotalWoddsCount, etc.)
  3. VideoLesson (which inherits from Lesson) (Properties: VideoUrl, Duration, etc.)

Almost everything is handled properly by EF Core, and I'm using the default Table-Per-Hierarchy (TPH) approach.

The problem arises when I want to retrieve lessons from the database and I need some of the shared columns between ArticleLesson and VideoLesson (i.e. some of the properties of Lesson), PLUS, some of the properties specific to either ArticleLesson or VideoLesson. Here's the expression I've come up with:

var r1 = dbContext.Lessons.Select<Lesson, LessonDto>(l =>
    l is ArticleLesson
    ? new ArticleLessonDto
    {
        Id = l.Id, // This is repeated below
        Title = l.Title, // This is repeated below
        // ...other properties that I would have to repeat below
        Content = (l as ArticleLesson).Content,
    }
    : l is VideoLesson
    ? new VideoLessonDto
    {
        Id = l.Id, // This is repeated above
        Title = l.Title, // This is repeated above
        // ...other properties that I would have to repeat above
        VideoUrl = (l as VideoLesson).VideoUrl,
    }
    : null
)
.ToList();

As you can see, I'm repeating the shared properties part, twice. There are just 2 properties that are being repeated in this example, Id and Title, but in the real world you can have dozens of these; and having to repeat all of them like this would be a h.

Is there any way to make this projection expression more succinct and avoid the repetition?

Upvotes: 4

Views: 1044

Answers (1)

joakimriedel
joakimriedel

Reputation: 1959

You could add a constructor to your LessonDto, ArticleLessonDto and VideoLessonDto that accepts the different shared properties.

    public class LessonDto
    {
        public LessonDto(int id, ... other)
        {
            Id = id;
            // ...
        }

        public int Id { get; set; }
    }

    public class ArticleLessonDto : LessonDto
    {
        public ArticleLessonDto(LessonDto dto) : base(dto.Id)
        {

        }

        public string Content { get; set; }
    }

    var r1 = dbContext.Lessons
        .Select(l => new
        {
            dto = new LessonDto(l.Id, ... other),
            full = l
        })
        .Select(row => row.full is ArticleLesson
        ? new ArticleLessonDto(row.dto)
        {
            Content = (row.full as ArticleLesson).Content,
        }
        : row.full is VideoLesson
        ? new VideoLessonDto(row.dto)
        {
            VideoUrl = (row.full as VideoLesson).VideoUrl,
        }
        : (LessonDto)null
    )
    .ToList();

Upvotes: 3

Related Questions