简体   繁体   English

如何在Linq中避免“选择n + 1”模式

[英]How to avoid “select n+1” pattern in Linq

I have a query (including LinqKit) of the form: 我有以下形式的查询(包括LinqKit):

Expression<Func<Country, DateTime, bool>> countryIndepBeforeExpr = 
  (ct, dt) => ct.IndependenceDate <= dt;
DateTime someDate = GetSomeDate();
var q = db.Continent.AsExpandable().Select(c =>
  new 
  {
    c.ID,
    c.Name,
    c.Area,
    Countries = c.Countries.AsQueryable()
                 .Where(ct => countryIndepBeforeExpr.Invoke(ct, someDate))
                 .Select(ct => new {ct.ID, ct.Name, ct.IndependenceDate})
  });

Now I want to iterate through q ... but since the Countries property of each element is of type IQueryable , it will be lazy loaded, causing n+1 queries to be executed, which isn't very nice. 现在我想通过迭代q ...但由于Countries的每个元素的属性的类型的IQueryable ,这将是懒加载,导致被执行N + 1个查询,这是不是很好。

What is the correct way to write this query so that all necessary data will be fetched in a single query to the db? 编写此查询以便所有必要的数据将在单个查询中提取到db的正确方法是什么?

EDIT 编辑

Hm, well it might have helped if I had actually run a Sql trace before asking this question. 嗯,如果我在问这个问题之前实际运行了Sql跟踪,那可能会有所帮助。 I assumed that because the inner property was of type IQueryable that it would be lazy-loaded... but after doing some actual testing, it turns out that Linq to Entities is smart enough to run the whole query at once. 以为 ,因为内财产是类型的IQueryable ,这将是懒加载...但是做了一些实际测试后,事实证明,LINQ到实体是足够聪明的同时运行整个查询。

Sorry to waste all your time. 很抱歉浪费您的全部时间。 I would delete the question, but since it already has answers, I can't. 我将删除该问题,但是由于它已经有了答案,所以不能。 Maybe it can serve as some kind of warning to others to test your hypothesis before assuming it to be true! 也许可以作为对他人的某种警告,在假设它成立之前对您的假设进行检验

Include countries to your model when you call for continents. 呼叫大洲时,将国家/地区包括在模型中。 With something like this: 用这样的东西:

var continents = db.Continent.Include(c => c.Countries).ToArray();

Then you can make your linq operations without iQueryable object. 然后,您可以在没有iQueryable对象的情况下进行linq操作。

I think this should work (moving AsExpandable() to root of IQueryable ): 认为这应该工作(将AsExpandable()移到IQueryable根目录):

var q = db.Continent
          .AsExpandable()
          .Select(c => new 
          {
              c.ID,
              c.Name,
              c.Area,
              Countries = c.Countries
                  .Where(ct => countryIndepBeforeExpr.Invoke(ct, someDate))
                  .Select(ct => new {ct.ID, ct.Name, ct.IndependenceDate})
});

If not, create two IQueryable and join them together: 如果没有,请创建两个IQueryable并将它们连接在一起:

var continents = db.Continents;
var countries = db.Countries
                  .AsExpandable()
                  .Where(c => countryIndepBeforeExpr.Invoke(c, someDate))
                  .Select(c => new { c.ID, c.Name, c.IndependenceDate });

var q = continents.GroupJoin(countries,
    continent => continent.ID,
    country => country.ContinentId,
    (continent, countries) => new
    {
        continent.ID,
        continent.Name,
        continent.Area,
        Countries = countries.Select(c => new
        {
            c.ID,
            c.Name,
            c.IndependenceDate
        })
    });

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

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