Linq 如何正确使用 where

4
这两个查询有什么区别?它们不是一样的吗?哪一个更快?在where之后再加where会有问题吗?对我来说,在where之后再加where会更容易阅读我的代码。我做错了吗?
第一个查询:
   Model= (model.Where(p => p.Date.Year == yilim)
                        .Where(p => p.Id== 2 || p.Id== 3)
                        .OrderBy(m => m.Date.Month))
                        .ToList();

第二个查询:
  Model= (model.Where(p => p.Date.Year == yilim && (p.Id== 2 || p.Id== 3))
                            .OrderBy(m => m.Date.Month))
                            .ToList();
5个回答

5

由于级联Where调用是逻辑上的AND操作,因此在功能上没有区别,您目前正在将两个单独的条件AND在一起。

然而,从效率上讲,这可能会稍微降低编译器优化条件检查的能力(例如短路)以及第二个Where所需的额外枚举器,而不仅仅是一个枚举器。 (抱歉,这部分只适用于Linq to Objects。)

如果您的代码要OR这些条件,则只有第二个查询会提供您想要的内容:

Model= (model.Where(p => p.Date.Year == yilim || p.Id== 2 || p.Id== 3)
                          .OrderBy(m => m.Date.Month))
                          .ToList();

// Cannot express the above query in simple daisy-chained Where calls.

如果Where中的逻辑变得难以阅读,请尝试使用“无注释编码”并将条件放入具有非常易读名称的方法中,然后您可以执行.Where(x => TheCustomerNameIsValid(x)),或者组合方法.Where(TheCustomerNameIsValid)(当您可以这样做时)。这还有助于调试,因为在方法中放置断点比在lambda中更加容易。

哇,我在有这么多经验之后才学习这个。在这些表达式中使用具有可读性的函数名称真是太聪明了。 - İsmet Alkan
@IsThatSo 嗯,这让我早上心情愉悦,干得好! :-) - Adam Houldsworth

3
第一个查询的区别在于第一个Where将应用于名为model的序列,并且将应用第二个where方法于生成的序列。而在第二个查询中,Where方法应用于名为model的序列。话虽如此,第二个查询比第一个更有效率,因为您只需通过model序列一次即可运行。
然而,认为第二个查询更有效率是基于理论的。我认为在实践中,您不会得到任何显着的、可测量的差异。

2

其他人给的答案非常清晰,但我有一个不同的建议。为什么不通过调试来查看生成的SQL查询结果呢?这样您就可以看到它们之间是否有差异。要获取系统生成和运行的实际SQL查询,您可以按如下方式编写:

using (EntityConnection con = new EntityConnection("Name = testEntities"))
{
    con.Open();

    using (testEntities db = new testEntities())
    {
        int yilim = 2013;

        IQueryable<Model> models = (db.Model.Where(p => p.Date.Year == yilim)
                                            .Where(p => p.ID == 2 || p.ID == 3)
                                            .OrderBy(m => m.Date.Month))
                                            .AsQueryable();

        string modelsQuery = ((System.Data.Objects.ObjectQuery)models).ToTraceString();

        IQueryable<Model> models2 = (db.Model.Where(p => p.Date.Year == yilim && 
                                                        (p.ID == 2 || p.ID == 3))
                                             .OrderBy(m => m.Date.Month))
                                             .AsQueryable();

        string modelsQuery2 = ((System.Data.Objects.ObjectQuery)models2).ToTraceString();

        System.IO.File.WriteAllText(@"C:\Users\username\Desktop\queries.txt", 
            "Query 1:\r\n" + modelsQuery + "\r\n" + 
            "Query 2:\r\n" + modelsQuery2);

    }

结果如下:

Query 1:
SELECT 
[Project1].[ID] AS [ID], 
[Project1].[Date] AS [Date]
FROM ( SELECT 
    DATEPART (month, [Extent1].[Date]) AS [C1], 
    [Extent1].[ID] AS [ID], 
    [Extent1].[Date] AS [Date]
    FROM [dbo].[Model] AS [Extent1]
    WHERE ((DATEPART (year, [Extent1].[Date])) = @p__linq__0) AND ([Extent1].[ID] IN (2,3))
)  AS [Project1]
ORDER BY [Project1].[C1] ASC


Query 2: 
SELECT 
[Project1].[ID] AS [ID], 
[Project1].[Date] AS [Date]
FROM ( SELECT 
    DATEPART (month, [Extent1].[Date]) AS [C1], 
    [Extent1].[ID] AS [ID], 
    [Extent1].[Date] AS [Date]
    FROM [dbo].[Model] AS [Extent1]
    WHERE ((DATEPART (year, [Extent1].[Date])) = @p__linq__0) AND ([Extent1].[ID] IN (2,3))
)  AS [Project1]
ORDER BY [Project1].[C1] ASC

而它们是完全一样的,因此这里不会有性能差异。


2
“Writing where after where makes my code more readable for me. Am I doing this wrong?”
“其他人已经评论了性能/实际编译的好处,我只是建议代码的可读性(这是一种宗教性质的建议,所以请自行斟酌)。”
“重新格式化第一个查询:”
   Model= (model.Where(p => p.Date.Year == yilim)
                .Where(p => p.Id== 2 || p.Id== 3)
                .OrderBy(m => m.Date.Month))
                .ToList();

我的偏好是将方法与它们被应用到的类对齐。一个多维语句可能看起来像这样:
   Model= (model.Where(p => p.Date.Where(d => d.Year == 2014)
                                  .Where(d => d.Month == 11))
                .Where(p => p.Id== 2 || p.Id== 3)
                .OrderBy(m => m.Date.Month))
                .ToList();

重新格式化的第二个查询:
  Model= (model.Where(p => p.Date.Year == yilim 
                           && (p.Id== 2 || p.Id== 3))
               .OrderBy(m => m.Date.Month))
               .ToList();

为了使代码中的 if like 操作更易读,我选择将所有的 OR 运算符放在一行上,而将所有的 AND 运算符放在单独的行上。
 Model = model.Where(m => (m.Name == "jon" || m.Name == "joe")
                          && (m.Color == "red" || m.Color == "blue"));

最后,我通常将lambda表达式的参数缩短为与变量第一个字母匹配的字母。

因此:

   Model= (model.Where(p => p.Date.Year == yilim)

变成:
   Model= (model.Where(m => m.Date.Year == yilim)

"对于更复杂的查询,我还使用了:"
   query = persons.Where(person => person == ...
                  .Where(person => person.Friends.Where(friend => 

有时候,单个字母很难与表达式相关联,而不必阅读整个表达式树正在做什么。

2
你不太可能注意到两者之间有任何明显的性能差异。调用 Where() 两次确实会增加一些开销: 你创建了一个额外的中间对象,在枚举期间当然还需要额外的方法调用来处理。
但在大多数代码中,这些差异是不会被注意到的。如果你发现将筛选器拆分为两个单独的调用 Where() 使代码更容易阅读和维护,那么这是更好的方法。
至少,在你真正遇到性能问题之前,这是对的。然后你可以回过头来重新审视它是否是你问题的一部分,并决定是否值得更改。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接