Reputation: 12814
I'm struggling with how to avoid repeating projection logic when using inheritance in EF Core.
Here's my scenario: I have three types:
Lesson
(which is an abstract class) (Properties: Id
, Title
, etc.)ArticleLesson
(which inherits from Lesson
) (Properties: Content
, TotalWoddsCount
, etc.)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
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