6

当我在编译的查询中包含一个函数时,幕后会发生什么,就像我在这里使用 DataConvert.ToThema() 将表对象转换为我的自定义业务对象一样:

public static class Queries
{
    public static Func<MyDataContext, string, Thema> GetThemaByTitle
    {
        get
        {
            var func = CompiledQuery.Compile(
                (MyDataContext db, string title) =>
                    (from th in elan.tbl_Thema
                     where th.Titel == title
                     select DataConvert.ToThema(th)).Single()
                     );
            return func;
        }
    }
}

public static class DataConvert
{
    public static Thema ToThema(tbl_Thema tblThema)
    {
        Thema thema = new Thema();

        thema.ID = tblThema.ThemaID;
        thema.Titel = tblThema.Titel;
        // and some other stuff

        return thema;
    }
}

并这样称呼它

Thema th = Queries.GetThemaByTitle.Invoke(db, "someTitle");

显然该函数没有翻译成 SQL 或其他东西(怎么可能),但是当我在 VS2010 中设置断点时它也不成立。

它可以正常工作,但我不明白如何或为什么。那里究竟发生了什么?

4

2 回答 2

2

您的DataConvert.ToThema()静态方法只是创建一个具有默认构造函数的类型的实例,并设置各种属性,对吗?如果是这样,它与以下内容并没有太大不同:

(from th in elan.tbl_Thema
where th.Titel == title
select new Thema{ID=th.ThemaID, Titel=th.Titel, etc...}
).Single());

当您调用 时Queries.GetThemaByTitle,正在编译一个查询。(顺便说一句,您调用它的方式可能会或可能不会实际上给您带来预编译的任何好处)。该“查询”实际上是一个代码表达式树,其中只有一部分用于生成发送到数据库的 SQL 代码。

它的其他部分将生成 IL 代码,该代码会获取从数据库返回的内容并将其放入某种形式以供您使用。LINQ(EF 或 L2S)足够聪明,能够接受您的静态方法调用并从中生成 IL 来执行您想要的操作 - 也许它是通过内部delegate或类似的方式来执行此操作的。但最终,它不需要与我在上面替换的内容有(太大)不同。

但请注意,无论您返回的是什么类型,都会发生这种情况;在某处,正在生成将 DB 值放入 CLR 对象的 IL 代码。那是那些表达式树的另一部分。


如果您想更详细地了解这些表达式树及其所涉及的内容,我将不得不为您挖掘,但我不确定您的问题是否是您正在寻找的。

于 2011-04-10T14:39:06.287 回答
1

让我首先指出,您是否编译查询并不重要。即使您没有预编译,您也会观察到相同的结果。

正如安德鲁指出的那样,从技术上讲,制作这项工作并不复杂。评估 LINQ 表达式时,会在内部构建表达式树。您的函数在此表达式树中显示为一个节点。这里没有魔法。您将能够在 L2S 和 L2E 中编写此表达式,并且可以正常编译和运行。直到您尝试对数据库实际执行实际的 SQL 查询。这就是差异开始的地方。L2S 似乎很乐意执行此任务,而 L2E 失败并出现 NotSupportedException,并报告它不知道如何将 ToThema 转换为商店查询。

那么里面发生了什么?在 L2S 中,正如 Andrew 所解释的,查询编译器理解您的函数可以与已执行的存储查询分开运行。因此,它将对您的函数的调用发送到对象读取管道(从 SQL 读取的数据转换为作为调用结果返回的对象)。

一旦事情 Andrew 不太正确,那就是你的静态方法里面的内容很重要。我认为不会。

如果您在调试器中为您的函数设置一个断点,您将看到它在每个返回的行中调用一次。在堆栈跟踪中,您将看到“轻量级函数”,这实际上意味着该方法是在运行时发出的。这就是 Linq to Sql 的工作原理。

Linq to Entity team seemed to go different route. I do not know, what was the reasoning, why they decided to ban all InvocationExpressions from L2E queries. Perhaps these were performance reason, or may be the fact that they need to support all kind of providers, not SQL Server only, so that data readers might behave differently. Or they simply thought that most people wouldn't realize that some of those are executed per returned row and preferred to keep this option closed.

Just my thoughts. If anyone has any more insight, please chime in!

于 2011-04-15T13:38:47.267 回答