我一直在尝试使用LINQ-SQL,试图获取可重复使用的表达式块,以便将其插入到其他查询中。因此,我从以下内容开始:
Func<TaskFile, double> TimeSpent = (t =>
t.TimeEntries.Sum(te => (te.DateEnded - te.DateStarted).TotalHours));
然后,我们可以在LINQ查询中使用上述内容,如下所示(以LINQPad示例为例):
TaskFiles.Select(t => new {
t.TaskId,
TimeSpent = TimeSpent(t),
})
这将产生预期的输出,但是,为插入的表达式生成了每行一个查询。这在LINQPad中可见。不好。
无论如何,我注意到了CompiledQuery.Compile
方法。虽然它需要一个DataContext
作为参数,但我想忽略它,并尝试相同的Func
。所以我最终得到了以下内容:
static Func<UserQuery, TaskFile, double> TimeSpent =
CompiledQuery.Compile<UserQuery, TaskFile, double>(
(UserQuery db, TaskFile t) =>
t.TimeEntries.Sum(te => (te.DateEnded - te.DateStarted).TotalHours));
请注意,我在这里没有使用db
参数。然而,现在当我们使用这个更新后的参数时,只生成了1个SQL查询。表达式成功地转换为SQL并包含在原始查询中。
所以我的最终问题是,是什么使得CompiledQuery.Compile
如此特殊?似乎DataContext
参数根本不需要,而此时我认为它更像是一个方便的参数来生成完整的查询。
像这样使用CompiledQuery.Compile
方法是否被认为是一个好主意?它似乎是一个大的黑客,但它似乎是LINQ重用的唯一可行路线。
更新
在Where
语句中使用第一个Func
时,我们会看到以下异常:
NotSupportedException: Method 'System.Object DynamicInvoke(System.Object[])' has no supported translation to SQL.
就像下面这样:
.Where(t => TimeSpent(t) > 2)
然而,当我们使用由CompiledQuery.Compile
生成的Func
时,查询成功执行并生成了正确的SQL。
我知道这不是重复使用Where
语句的理想方式,但它展示了一下表达式树是如何生成的。
CompiledQuery.Compile
的行为是默认行为。我将不得不使用反射器亲眼看看确切的科学原理。感谢您的回答。 - Hux