gpt4 book ai didi

linq-to-sql - LINQ-SQL 重用 - CompiledQuery.Compile

转载 作者:行者123 更新时间:2023-12-03 22:39:56 28 4
gpt4 key购买 nike

我一直在使用 LINQ-SQL,试图获得可以热插入其他查询的可重用表达式 block 。所以,我从这样的事情开始:

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 重用的唯一可行途径。

更新

使用第一个 FuncWhere 内声明,我们看到以下异常:
NotSupportedException: Method 'System.Object DynamicInvoke(System.Object[])' has no supported translation to SQL.

如下所示:
.Where(t => TimeSpent(t) > 2)

但是,当我们使用 FuncCompiledQuery.Compile 生成,查询成功执行,生成正确的SQL。

我知道这不是重复使用 Where 的理想方式语句,但它显示了如何生成表达式树。

最佳答案

执行摘要:
Expression.Compile生成 CLR 方法,其中 CompiledQuery.Compile生成一个作为 SQL 占位符的委托(delegate)。

直到现在您还没有得到正确答案的原因之一是您的示例代码中的某些内容不正确。如果没有数据库或通用样本,其他人可以玩的机会会进一步减少(我知道很难提供,但通常是值得的)。

谈事实:

Expression<Func<TaskFile, double>> TimeSpent = (t =>
t.TimeEntries.Sum(te => (te.DateEnded - te.DateStarted).TotalHours));

Then, we can use the above in a LINQ query like the below:

TaskFiles.Select(t => new {
t.TaskId,
TimeSpent = TimeSpent(t),
})


(注意:也许您对 TimeSpent 使用了 Func<> 类型。这会产生与以下段落中概述的场景相同的情况。请务必阅读并理解它)。

不,这不会编译。不能调用表达式( TimeSpent 是一个表达式)。他们需要首先被编译成一个委托(delegate)。当您调用 Expression.Compile() 时会发生什么是表达式树被编译成 IL,它被注入(inject)到 DynamicMethod 中。 ,然后你会得到一个代表。

以下将起作用:
var q = TaskFiles.Select(t => new {
t.TaskId,
TimeSpent = TimeSpent.Compile().DynamicInvoke()
});

This produces the expected output, except, a query per row is generated for the plugged expression. This is visible within LINQPad. Not good.



为什么会这样?那么,Linq To Sql 将需要获取所有 TaskFiles , 脱水 TaskFile实例,然后在内存中针对它运行您的选择器。您可能会收到每个 TaskFile 的查询,因为它们包含一个或多个 1:m 映射。

虽然 LTS 允许在内存中进行选择以进行投影,但对于 Wheres 则不这样做(据我所知,这是需要引用的)。仔细想想,这很有意义:通过过滤内存中的整个数据库,然后在内存中转换其中的一个子集,您可能会传输更多数据。 (尽管如您所见,它会产生查询性能问题,但在使用 ORM 时需要注意一些事情)。
CompiledQuery.Compile()做一些不同的事情。它将查询编译为 SQL,它返回的委托(delegate)只是 Linq to SQL 将在内部使用的占位符。您不能在 CLR 中“调用”此方法,它只能用作另一个表达式树中的节点。

那么为什么 LTS 会使用 CompiledQuery.Compile 生成有效的查询? 'd 表达呢?因为它知道这个表达式节点做什么,因为它知道它背后的 SQL。在 Expression.Compile案例,它只是一个 InvokeExpression调用 DynamicMethod正如我之前解释的那样。

为什么它需要一个 DataContext 参数?是的,创建完整查询更方便,但这也是因为表达式树编译器需要知道用于生成 SQL 的映射。没有这个参数,找这个映射会很痛苦,所以这是一个非常明智的要求。

关于linq-to-sql - LINQ-SQL 重用 - CompiledQuery.Compile,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6479552/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com