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

时间:2014-03-04 14:26:38

标签: c# linq linq-to-entities entity-framework-5 linqkit

我有一个表格的查询(包括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})
  });

现在我想迭代q ...但由于每个元素的Countries属性都是IQueryable类型,它将被延迟加载,导致n + 1个查询被执行,这不是很好。

编写此查询的正确方法是什么,以便在单个查询中将所有必要的数据提取到数据库?

修改

嗯,如果我在问这个问题之前实际运行了Sql跟踪,它可能会有所帮助。我假设,因为内部属性类型为IQueryable,它会延迟加载...但经过一些实际测试后,事实证明Linq to Entities足够智能立即运行整个查询。

抱歉浪费你所有的时间。我会删除这个问题,但由于它已经有答案,我不能。也许它可以作为某种警告,让其他人在假设它是真的之前测试你的假设!

2 个答案:

答案 0 :(得分:1)

在呼叫大陆时,请将国家/地区包含在您的模型中。有这样的事情:

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

然后你可以在没有iQueryable对象的情况下进行linq操作。

答案 1 :(得分:0)

认为这应该有用(将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})
});

如果没有,请创建两个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
        })
    });