我正在尝试使用EF Core DbContext筛选ASP.NET Core 2.1中的数据。场景如下: 我们的主要实体是电影。电影具有一种或多种类型,并且一种类型可以属于一种或多种电影。电影和演员也是如此。因此,我们有两个多对多关系(代码优先),描述这些关系的代码为:
public class Movie
{
public int Id {get; set;}
public string Title {get; set;}
public ICollection<MovieActor> MovieActors {get; set;}
public ICollection<MovieGenre> MovieGenres {get; set;}
}
public class Actor
{
public int Id {get; set;}
public string Name {get; set;}
public ICollection<MovieActor> MovieActors {get; set;}
}
public class Genre
{
public int Id {get; set;}
public string Name {get; set;}
public ICollection<MovieGenre> MovieGenres {get; set;}
}
public class MovieActor
{
public int MovieId {get; set;}
public Movie Movie {get; set;}
public int ActorId {get; set;}
public Actor Actor {get; set;}
}
public class MovieGenre
{
public int MovieId {get; set;}
public Movie Movie {get; set;}
public int GenreId {get; set;}
public Genre Genre {get; set;}
}
负责处理数据库查询的上下文是MoviesDbContext。 我正在尝试根据两个整数列表来过滤“电影”表中的所有数据,这两个整数表示数据库中演员和体裁的ID。
List<int> actorIds;
List<int> genreIds;
对于过滤,我们希望获得所有同时遵循以下规则的电影:
1)所有演员列表中包含至少一个演员ID在“ actorIds”列表中的演员的电影
2)所有其流派列表中至少包含一个在“ genreIds”列表中找到其ID的流派的电影
我找到的解决方案如下:
context.Movies
.Include(m => m.MovieActors)
.Include(m => m.MovieGenres)
.Where(m => actorIds.Any(id => m.MovieActors.Any(ma => ma.Id == id)))
.Where(m => genreIds.Any(id => m.MovieGenres.Any(mg => mg.Id == id)));
这可以进行正确的过滤,但是问题是,当EF Core将代码转换为sql命令时,它将查询分解为很多小查询,这会导致严重的性能问题,有些查询要花费数十秒钟。 如何重构它,使其仅在一个查询中进行过滤?
答案 0 :(得分:2)
在对扩展方法进行了一些实验之后,我发现仅执行N + 1个查询,而不执行数千个查询的东西,其中N是多对多关系的数量。
因此,不要在where()扩展方法中使用此lambda:
m => actorIds.Any(id => m.MovieActors.Any(ma => ma.Id == id))
您必须使用这个:
m => m.MovieActors.Any(ma => actorIds.Contains(ma.ActorId))
经过研究,我发现EF Core仍不完整,无法克服N + 1问题。但是,N + 1个查询而不是数千个查询还是一个很大的进步。