修剪Where子句中的功能

时间:2016-01-26 11:30:25

标签: c# linq

假设您有一个项目集合

    IList<string> items = List<string>();

并且您希望针对某个搜索字词搜索每个元素: term 。如果你在Where子句中执行Trim(),如下所示,是否会对序列中的每个元素进行修剪操作,还是会编译一次,然后用于检查元素?

    items.Where(o => o.Contains(term.Trim())).ToList(); 

这个问题是基于LINQ to SQL语句但我简化了它。 (不确定编译为SQL会有什么不同。)

4 个答案:

答案 0 :(得分:3)

如果它在内存中,那么,每次调用它。一个简单的测试:

public static class BLah
{
    public static string DoTrim(this string item)
    {
        Console.WriteLine("called");
        return item.Trim();
    }
}

IList<string> items = new List<string> { "a", "b", "c" };
items.Where(o => o.Contains(term.DoTrim()));

打印出来的&#39;叫做&#39; 3次。

但是,在数据库上执行时,它完全由ORM 决定如何生成SQL,这可能会或可能不会调用TRIM()作为查询。唯一知道的方法是测试它并查看它生成的SQL。

对于Linq2Sql,它多次运行修剪。例如:

string term = "a b ";
Warehouses.Where(w => w.AdminComment.Contains(term.DoTrim()));

可生产

-- Region Parameters
DECLARE @p0 NVarChar(1000) = '%a b%'
-- EndRegion
SELECT [t0].[Id], [t0].[Name], [t0].[AdminComment], [t0].[AddressId]
FROM [Warehouse] AS [t0]
WHERE [t0].[AdminComment] LIKE @p0

答案 1 :(得分:2)

值得注意的是,使用Func<…>的基于对象的linq和使用Expression<Func<…>>的基于表达式的linq之间存在差异。

在基于对象的linq中,o => o.Contains(term.Trim())用作Func<string, bool>。我们可以这样推理它。

现在,考虑o => o.Contains(term.Trim())。它有一个捕获的变量term,调用方法Trim()。这就是发生的事情。

每次必须调用Trim()有三个原因。

  1. 只考虑这个委托,我们不知道Trim()是纯粹的,因此总是会为同一个对象返回相同的值。
  2. 考虑到这个委托,我们不知道term是不可变的,因此Trim()会为同一个对象返回相同的值,即使我们知道它是纯粹的。
  3. 仅考虑此代表,我们不知道所调用的变量在调用之间没有变化。
  4. 如果我们认为捕获的变量是可变字段,那么所有委托都可以被推理为非常类似于方法。如果你看看:

    class SomeClass
    {
      public string term;
      public bool Predicate(string o)
      {
        return o.Contains(term.Trim());
      }
    }
    

    由于上述三个原因,您不会期望缓存Trim()的来电。

    现在,使用基于表达式的linq,例如Linq2SQL或EF,它有点不同。

    在这种情况下,o => o.Contains(term.Trim())用作Expression<Func<string, bool>>,并且提供商可能会对此做出更多变化。

    1. 提供者可能具有特殊知识,知道term.Trim()只能拥有一个值,并将表达式重写为o => o.Contains("/* result of single call to term.Trim() goes here */")
    2. 提供商可能不知道如何处理Trim()并抛出异常。
    3. 提供程序可能没有对它如何将term.Trim()转换为SQL做任何特别的事情,但是数据库处理SQL本身可能会实现它只需要计算一次,所以这样做。
    4. 以上都不会发生,因为它与基于对象的情况基本相同。
    5. 在优化处理方面可能会或可能不会发生的情况下,表达式处理更加灵活。

答案 2 :(得分:1)

为什么term.Trim的结果应该以任何方式缓存?没有包含运行时魔法,这意味着此操作的结果永远不会改变,因此始终对每个元素执行操作。要改进这一点,您可以自己缓存结果:

var test = term.Trim();
items.Where(o => o.Contains(test).ToList(); 

答案 3 :(得分:0)

这不是L2S IQueryable调用。如果它是一个正确的L2S呼叫,那么它将被转换为:

字段LIKE @ p0

其中@ p0设置为%term%

例如,使用Northwind示例数据库:

var term = "USA";
var items = db.Customers
   .Where(c => c.Address.Contains( term.Trim() ))
   .ToList();

会产生(感谢LinqPad):

-- Region Parameters
DECLARE @p0 NVarChar(1000) SET @p0 = '%USA%'
-- EndRegion
SELECT [t0].[CustomerID], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Country], [t0].[Phone], [t0].[Fax]
FROM [Customers] AS [t0]
WHERE [t0].[Address] LIKE @p0

可能你可能想把它写成这个:

var term = "USA".Trim();
var items = Customers
   .Where(c => c.Address.Contains( term ))
   .ToList();

生成的SQL将是相同的。