我有3个嵌套的实体,TvShow, Season和Episodes:
public class TvShow
{
public Id { get; set; }
public List<Season> Seasons { get; set; }
}
public class Season
{
public Id { get; set; }
public int TvShowId { get; set; }
public TvShow TvShow { get; set; }
public List<Episode> Episodes { get; set; }
}
public class Episode
{
public Id { get; set; }
public int TvShowId { get; set; }
public TvShow TvShow { get; set; }
public int SeasonId { get; set; }
public Season Season { get; set; }
}
我想写一个查询,我可以给出以下输入:
public List<int> TvShowIds { get; }
public List<int> TvShowSeasonIds { get; }
public List<int> TvShowEpisodeIds { get; }
这就是我卡住的地方,查询应该包括所有不同类型的id及其子导航。
。对于每个TvShowId
,它应该返回包含所有Seasons
和Episodes
的TvShow
实体。对于每个SeasonId
,它应该包括所有的Episodes
和它的父TvShow
。对于每个EpisodeId
,它应该包括它的父Season
和TvShow
,而不包括属于同一Season
的任何其他Episodes
。
那么所有这些都应该作为合并(联合?)嵌套的List<TvShow>()
返回,没有任何重复。
我试过不同的方法,但每次都卡住了。这应该不难,也许我在思考过程中遗漏了什么?
非常感谢你的帮助!
假设您有(最多)3个不同的过滤需求,这可能应该使用单独的查询来完成,因为您的代码将需要处理一些警告条件。例如,我可能从我已经指定了一个节目或季节ID的季节或节目中选择一个插曲ID,在这种情况下,我不想重复该节目。
那么所有这些都应该作为合并(联合?)嵌套列表()返回,没有任何重复项。
还有一种可能性,对于给定的节目,我可能会通过季节过滤器来选择它,比如《巴比伦5》的第二季,但然后给出一个插曲过滤器,从第四季中选择第三集。声音的事情你可能想检索巴比伦5号(显示)的第2季,还有四季集# 3。
因为我们可能需要选择由各自的剧集组成的多个季节,所以最好从剧集中攻击它,然后从显示端重新组合视图,应该只列出所需的各个季节。
关于这种方法而不是投影的一个警告是,生成的实体将不再是数据的完整或可完整表示。通过选择节目和季节,仅包含所需剧集的子集,节目和季节不再反映完整的剧集列表。如果将这个实体传递给任何方法,希望对一个完整的Show/w季和集的表示进行操作,请小心。
List<Episodes> episodes = new List<Episode>();
IEnumerable<int> existingEpisodeIds = new List<int>();
if (filter.TvShowsIds.Any())
{
episodes.AddRange(Context.Episodes
.Include(x => x.Season)
.ThenInclude(x => x.TvShow)
.Where(x => filter.TvShowIds.Contains(x.Season.TvShow.Id))
.ToList());
existingEpisodeIds = episode.Select(x => x.id).ToList();
}
if (filter.SeasonIds.Any())
{
episodes.AddRange(Context.Episodes
.Include(x => x.Season)
.ThenInclude(x => x.TvShow)
.Where(x => !existingEpisodeIds.Contains(x.Id)
&& filter.SeasonIds.Contains(x.Season.Id)
.ToList());
existingEpisodeIds = episodes.Select(x => x.id).ToList();
}
if (filter.Episodes.Any())
{
episodes.AddRange(Context.Episodes
.Include(x => x.Season)
.ThenInclude(x => x.TvShow)
.Where(x => !existingEpisodeIds.Contains(x.Id)
&& filter.EpisodeIds.Contains(x.Id)))
.ToList();
}
var shows = episodes.Select(x => x.Season.Show).Distinct().ToList();
通过攻击这集和立即加载相应的季节,在这个方向,相关的季节将会出现对于每一个选择集,将TvShow。这意味着只有那些至少有一集被选中的剧集才会出现在结果节目中。我们跟踪选定的剧集id,并在随后的搜索中刷新它,以避免重复返回的剧集。(例如,如果第二季的所有剧集都已被选中,则选择第二季的第3集,或者该节目的所有剧集)
这种方法的警告是,它假设所有的节目都有季节,所有的季节都有剧集,我认为这是一个安全的假设。例如,如果你有一个TvShowId #99,没有季节或剧集,并且TvShowId过滤器为99,它将不会被返回,因为我们正在查询剧集。
假设我已经正确理解了你的问题,我怀疑EF core将能够翻译,除非它在内存中完成,但你可以试试这个:
await Db.TvShows
.Where(x => TvShowIds.Contains(x.Id))
.Select(x => { x.Seasons = x.Seasons.Where(y => TvShowSeasonsIds.Contains(y.Id))
.Select(y => { y.Episodes = y.Episodes.Where(z => TvShowEpisodeIds.Contains(z.Id)).ToList(); return y; }).ToList(); return x; })
.ToListAsync();
编辑,我刚刚在。net Core 3.1中尝试了以下内容,它似乎有效。如果您需要相关的实体向上移动,您可以添加到select中。你可能应该使用dto而不是实际的EF核心实体创建数据传输对象(dto)
await Db.TvShows
.Where(x => TvShowIds.Contains(x.Id))
.Select(x => new TvShow
{
Id = x.Id,
Seasons = x.Seasons.Where(y => TvShowSeasonsIds.Contains(y.Id))
.Select(y => new Season
{
Id = y.Id,
TvShowId = y.TvShowId,
Episodes = y.Episodes.Where(z => TvShowEpisodeIds.Contains(z.Id)).ToList(),
}).ToList()
}).ToListAsync();