[英]Manual Include in EF Core
我有以下結構: Training
has many Module
has many Phase
has many Question
。
我使用以下查詢來獲取上述內容
Context.Trainings
.Include(x => x.Modules)
.ThenInclude(x => x.Phases)
.ThenInclude(y => y.Questions)
Question
也有很多Comment
,但這種關系沒有被定義為導航屬性,因為Comment
可以有不同類型的專利。 所以Comment
只有一個ParentId
,它有時是Question
,有時是其他東西。
我的問題是如何修改上述查詢,以對每個Question
計算來自Context.Comments
的子Comment
並將其分配給Question.CommentCount
? 有點像手冊Include
在我的腦海里是這樣的
Context.Trainings
.Include(x => x.Modules)
.ThenInclude(x => x.Phases)
.ThenInclude(y => y.Questions.Select(x=> new Question.Question {
Name = x.Name,
Description = x.Description,
CommentCount = Context.Comments.Where(y=>y.ParentId == x.Id)
}));
但似乎您不能將預測放入Include
中,我不知道如何以另一種方式考慮這一點。
隨着實體的建立,例如...
public class Training
{
public int Id { get; set; }
public ICollection<Module> Modules { get; set; }
}
public class Module
{
public int Id { get; set; }
public ICollection<Phase> Phases { get; set; }
}
public class Phase
{
public int Id { get; set; }
public ICollection<Question> Questions { get; set; }
}
public class Question
{
public int Id { get; set; }
[NotMapped]
public int CommentCount { get; set; }
}
public class Comment
{
public int Id { get; set; }
public int ParentId { get; set; }
}
// DbContext
public DbSet<Training> Trainings { get; set; }
public DbSet<Module> Modules { get; set; }
public DbSet<Phase> Phases { get; set; }
public DbSet<Question> Questions { get; set; }
public DbSet<Comment> Comments { get; set; }
...它可以在一個查詢中完成,但它非常混亂。
// query all nested navigations using projections with extra data
var projected = await context.Trainings
.Select(t =>
new
{
Training = t,
Modules = t.Modules.Select(m =>
new
{
Module = m,
Phases = m.Phases.Select(p =>
new
{
Phase = p,
Questions = p.Questions.Select(q =>
new
{
Question = q,
CommentCount = context.Comments.Count(c => c.ParentId == q.Id)
}
)
}
)
}
)
}
)
.ToListAsync();
// fixup by setting comment count from dto projection to "real" tracked entity
foreach (var q in projected.SelectMany(t => t.Modules).SelectMany(m => m.Phases).SelectMany(m => m.Questions))
{
q.Question.CommentCount = q.CommentCount;
}
// thanks to ef core entity tracker this will still work
var trainings = projected.Select(p => p.Training);
var totalCommentCount = trainings.SelectMany(t => t.Modules).SelectMany(m => m.Phases).SelectMany(p => p.Questions).Sum(q => q.CommentCount);
最終查詢
SELECT [t].[Id], [t0].[Id], [t0].[TrainingId], [t0].[Id0], [t0].[ModuleId], [t0].[Id00], [t0].[PhaseId], [t0].[c]
FROM [Trainings] AS [t]
LEFT JOIN (
SELECT [m].[Id], [m].[TrainingId], [t1].[Id] AS [Id0], [t1].[ModuleId], [t1].[Id0] AS [Id00], [t1].[PhaseId], [t1].[c]
FROM [Modules] AS [m]
LEFT JOIN (
SELECT [p].[Id], [p].[ModuleId], [q].[Id] AS [Id0], [q].[PhaseId], (
SELECT COUNT(*)
FROM [Comments] AS [c]
WHERE [c].[ParentId] = [q].[Id]) AS [c]
FROM [Phases] AS [p]
LEFT JOIN [Questions] AS [q] ON [p].[Id] = [q].[PhaseId]
) AS [t1] ON [m].[Id] = [t1].[ModuleId]
) AS [t0] ON [t].[Id] = [t0].[TrainingId]
ORDER BY [t].[Id], [t0].[Id], [t0].[Id0]
正如評論中所指出的,您可以從使用 TPH 和真正的導航集合返回問題評論中受益,並且您可能還應該使用拆分查詢或多個查詢,而不是像這樣將它們全部連接起來。 但根據用例,也許單個查詢對您來說可能會更好。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.