简体   繁体   English

LINQ查询,n + 1个问题

[英]LINQ query with n+1 issue

I have a query with three sub queries and my problem is that the sub queries are run for each Country (n+1). 我有一个包含三个子查询的查询,但我的问题是,每个国家(n + 1)都需要运行子查询。

I have boiled down my query to make it easier to read, since the main query is around 70 rows, and changed the domain to make it more understandable. 由于主要查询大约有70行,因此我简化了查询以使其更易于阅读,并更改了域以使其更易于理解。

I've tried including Cities/Mountains/Rivers and running .ToList() on the sub queries, but to no avail. 我试过包括城市/山/河,并在子查询上运行.ToList(),但无济于事。

// The CountryDto class I'm selecting to.
public class CountryDto
{
    public string CountryName { get; set; }
    public IEnumerable<CityDto> CityDtos { get; set; }
    public IEnumerable<MountainDto> MountainDtos { get; set; }
    public IEnumerable<RiverDto> RiverDtos { get; set; }
}
// The query
var query = _db.Countries
    .Select(country => new CountryDto
    {
        CountryName = country.Name,
        CityDtos = country.Citites
            .Where(city => city.Population > 10000)
            .Select(city => new CityDto
            {
                Name = city.Name,
            }),
        MountainDtos = country.Mountains
            .Where(mountain => mountain.Height > 100)
            .Select(mountain => new MountainDto
            {
                Name = mountain.Name,
            }),
        RiverDtos = country.Rivers
            .Where(river => river.Length > 1000)
            .Select(river => new RiverDto
            {
                Name = river.Name,
            }),
    })
    .Where(c => c.CityDtos.Any() || c.MountainDtos.Any() || c.RiverDtos.Any());

var totalCount = query.Count();
var countries = await query.ToListAsync();

Entity Framework Core supports parts of the query being evaluated on the client and parts of it being pushed to the database. Entity Framework Core支持在客户端上评估查询的一部分并将其推送到数据库。 It is up to the database provider to determine which parts of the query will be evaluated in the database. 由数据库提供者决定将在数据库中评估查询的哪些部分。

In your case, all .Any parts are evaluated in the client side I guess. 就您而言,所有.Any零件都在客户端进行评估。 You can configure your code to throw an exception for client evaluation. 您可以配置代码以引发异常以进行客户端评估。

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFQuerying;Trusted_Connection=True;")
        .ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning));
}

For more info https://docs.microsoft.com/en-us/ef/core/querying/client-eval 有关更多信息, 请访问https://docs.microsoft.com/zh-cn/ef/core/querying/client-eval

Ended up splitting the query into a .Where() part for the count and a .Select() part for the result, which removed my n+1 issue. 最终将查询分为.Where()部分(用于计数)和.Select()部分(用于结果),这消除了我的n + 1问题。

var query = await _db.Countries
    .Include(c => c.Cities)
    .Include(c => c.Mountains)
    .Include(c => c.Rivers)
    .Where(c => c.Cities.Any(city => city.Population > 10000)
        || c.Mountains.Any(mountain => mountain.Heigh > 1000)
        || c.River.Any(river => river.Length > 100000))
    .Where(c => c.Cities.Any() || c.Mountains.Any() || c.Rivers.Any())
    .ToListAsync();

var totalCount = query.Count();

var countries = query
    .Select(country => new CountryDto
    {
        CountryName = country.Name,
        CityDtos = country.Citites
            .Select(city => new CityDto
            {
                Name = city.Name,
            }),
        MountainDtos = country.Mountains
            .Select(mountain => new MountainDto
            {
                Name = mountain.Name,
            }),
        RiverDtos = country.Rivers
            .Select(river => new RiverDto
            {
                Name = river.Name,
            }),
    })
    .ToList();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM