【问题标题】:Where clause on collection集合的 Where 子句
【发布时间】:2012-12-16 16:40:16
【问题描述】:

我正在使用 Julie Lerman 的 DbContext 书中的 BAGA 代码。我想在 LINQ 中重新创建以下 SQL 查询并将结果放入 List 集合中并且遇到问题。 http://learnentityframework.com/downloads/

SELECT * FROM baga.Locations d
LEFT JOIN Lodgings l ON d.LocationID = l.destination_id
WHERE d.Country = 'usa'
AND (l.MilesFromNearestAirport > 5 or l.MilesFromNearestAirport is null)

因此,用英语,获取位于美国的所有位置(目的地),并包括 MilesFromNearestAirport > 5 的所有相关住宿

语法无法编译,但我希望类似于下面的内容

var dests = context.Destinations
  .Where(d => d.Country == "USA" && d.Lodgings.Where(l => l.MilesFromNearestAirport > 5))
  .Select(d => d)
  .ToList();

有什么想法吗?

【问题讨论】:

  • 方法不对,因为左连接。你需要做一些类似的事情: .Where(d => d.Country == "USA" && (d.Lodgings.Count == 0 || d.Lodgings.Any(l => l.MilesFromNearestAirport == null || l.MilesFromNearestAirport > 5)) 这应该检索正确的位置。访问住宿时,需要使用 lodgings where 子句中的相同表达式来检索适用的住宿。据我所知,实体框架仅支持内部连接. 另一方面,这个查询在 nhibernate 中非常简单。
  • 这些解决方案都不起作用。 Shelkel,你用过 NHibernate 很多吗?你会推荐它而不是 EF 吗?
  • 只是好奇...为什么在这里需要 .Select(d => d)。如果你删除它,它只是一个直接的 lambda 表达式,可能会起作用。另一个快速问。这取决于个人喜好,但您可以关闭 && 部分的方括号并将 && 替换为另一个 .Where(...)。
  • 我写这篇文章已经有一段时间了,但是我的原始代码不起作用,我只是在学习 Linq,所以请忽略它!我现在可能会使用某种带有 where 子句的 groupjoin 来表示 null & > 5。原来的 Select(d => d) 对我来说似乎毫无意义;-)

标签: c# .net entity-framework linq-to-entities


【解决方案1】:

正如@Sampath 所说,这通常是通过导航属性完成的,但我相信他的代码并不完全符合您的要求。这(我认为)更接近:

var dests = context.Destinations
           .Where(d => d.Country == "USA")
           .Select(d => 
               new { d,
                     RemoteLodgings = d.Lodgings
                         .Where(l => l.MilesFromNearestAirport > 5)}
           .ToList();

但是如果我把你的要求放在信里,它仍然不能满足你的要求。您想要 包含 住宿的位置,即部分加载导航属性。当实体要被序列化并在一个包中发送到(Web)客户端时,这会很方便。 (虽然上面的方法也可以)。

但是可以使用部分加载的导航属性创建一个集合。

这本书在第 40 页展示了如何部分加载单个实体的导航属性(简而言之:context.Entry(entity).Collection(e => e.Children).Query().Where(condition)。但如前所述,这只是一个实例。对于集合来说,这不是最好的方法实体。

有趣的是(正如我的一位同事发现的那样),您可以通过将所需的集合元素分别加载到上下文中来轻松地为实体集合执行此操作:

var lodgings = context.Lodgings
              .Where(l => l.MilesFromNearestAirport > 5 
                       && l.Destination.Country == "USA")
              .ToList();

现在,如果您循环访问context.Destinations.Where(d => d.Country == "USA"),您将看到他们的住所加载了“>5”。可能是因为此时 EF 执行了关系修复。 (延迟加载已禁用,因为延迟加载将完全加载导航属性)。

编辑(在您发表评论后)
当你说这有点骇人听闻时,我完全同意。实际上,我一开始就忘了提到这一点。问题是,当延迟加载碰巧被不知道代码用途的人激活时,整个机制就会崩溃。我不喜欢以不明显的方式依赖于状态的代码。所以我总是更喜欢第一种方法。

【讨论】:

  • 说我已经通过删除 var lodgings 并将 .ToList() 替换为 Load() 稍微改进了您的代码,因为我想这就是我需要做的。实际上现在有点开心了。
  • 哦,我不得不删除 virtual 关键字来关闭延迟加载(就像你说的那样)
  • 另外,你对仅仅使用存储过程而不是实体框架有什么想法?
  • 同意!!如果这是对性能敏感的东西,您可以考虑使用存储过程。我不会很快选择它,因为我更愿意拥有一个代码库。当然,如果有更多的存储过程,它可能是一个可行的选择。
  • 我发现延迟加载的前提在某些情况下非常好(在本地处理数据等),但我不想这样做,因为我想将 JSON 数据返回给客户端(正如你正确猜到了)所以我需要一次性检索更大的数据块。
【解决方案2】:

这通常是通过使用导航属性完成的,导航属性会在您获取实体时加载。

但是,您也可以使用以下方法执行此操作:

(from d in baga.Locations
 from l in Lodgings
 where (d.LocationID == l.destination_id)
 where (d.Country = 'usa' && (l.MilesFromNearestAirport > 5 || l.MilesFromNearestAirport == null))
 select d)
 .ToList();

希望对你有帮助。

【讨论】:

  • 对不起,结果集不正确。这既不限制 MilesFromNearestAirport > 5 也不包括那些没有相关 Lodgings 文件的记录。是我自己还是 LINQ 对这样简单的 sql 很痛苦?
【解决方案3】:

使用 LINQ 连接怎么样?

 var res = from d in context.Destinations
           join l in context.Lodgings on d.LocationID equals l.destination_id
           where (l.MilesFromNearestAirport > 5 || l.MilesFromNearestAirport == null)
                 && d.Country = "usa"
           select new {
                     Destination = d,
                     Location = l
                  }

【讨论】:

    猜你喜欢
    • 2017-11-12
    • 2018-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多