如何创建一个英孚查询返回一个嵌套列表的导航属性id基于不同的实体?



我有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,它应该返回包含所有SeasonsEpisodesTvShow实体。对于每个SeasonId,它应该包括所有的Episodes和它的父TvShow。对于每个EpisodeId,它应该包括它的父SeasonTvShow,而不包括属于同一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();

相关内容

  • 没有找到相关文章

最新更新