假设您有一个项目集合
IList<string> items = List<string>();
并且您希望针对某个搜索字词搜索每个元素: term 。如果你在Where子句中执行Trim(),如下所示,是否会对序列中的每个元素进行修剪操作,还是会编译一次,然后用于检查元素?
items.Where(o => o.Contains(term.Trim())).ToList();
这个问题是基于LINQ to SQL语句但我简化了它。 (不确定编译为SQL会有什么不同。)
答案 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()
有三个原因。
Trim()
是纯粹的,因此总是会为同一个对象返回相同的值。term
是不可变的,因此Trim()
会为同一个对象返回相同的值,即使我们知道它是纯粹的。如果我们认为捕获的变量是可变字段,那么所有委托都可以被推理为非常类似于方法。如果你看看:
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>>
,并且提供商可能会对此做出更多变化。
term.Trim()
只能拥有一个值,并将表达式重写为o => o.Contains("/* result of single call to term.Trim() goes here */")
Trim()
并抛出异常。term.Trim()
转换为SQL做任何特别的事情,但是数据库处理SQL本身可能会实现它只需要计算一次,所以这样做。在优化处理方面可能会或可能不会发生的情况下,表达式处理更加灵活。
答案 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将是相同的。