使用EF Core在单个查询中过滤数据

时间:2018-07-17 19:52:47

标签: c# asp.net-core entity-framework-core

我正在尝试使用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命令时,它将查询分解为很多小查询,这会导致严重的性能问题,有些查询要花费数十秒钟。 如何重构它,使其仅在一个查询中进行过滤?

1 个答案:

答案 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个查询而不是数千个查询还是一个很大的进步。