包括“offline"编译查询中的代码

本文关键字:查询 代码 编译 offline quot 包括 | 更新日期: 2023-09-27 17:49:27

当我在编译后的查询中包含一个函数时,会发生什么情况,就像我在这里使用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中设置断点时,它也不成立。

它工作没有问题,但我不明白如何或为什么。到底发生了什么?

包括“offline"编译查询中的代码

您的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代码。这是表达式树的另一部分。


如果你想更详细地了解这些表达式树和它们所涉及的内容,我必须为你挖掘,但从你的问题中我不确定这是否是你正在寻找的。

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

从技术上讲,正如Andrew指出的那样,使这项工作并不那么复杂。当计算LINQ表达式时,将在内部构造表达式树。您的函数在此表达式树中显示为节点。这里没有魔法。您将能够在L2S和L2E中编写这个表达式,并且它可以很好地编译和运行。直到您尝试对数据库执行实际的SQL查询。这就是差异开始的地方。L2S似乎很高兴地执行这个任务,而L2E失败了,出现NotSupportedException,并报告它不知道如何将ToThema转换为store查询。

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

Andrew说的有一点不太对,那就是静态方法内部的内容很重要。我不这么认为。

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

Linq to Entity团队似乎走了不同的路线。我不知道原因是什么,为什么他们决定禁止L2E查询中的所有InvocationExpressions。这可能是性能原因,也可能是他们需要支持所有类型的提供者,而不仅仅是SQL Server,这样数据阅读器的行为就会有所不同。或者他们只是认为大多数人不会意识到其中一些是按返回的行执行的,所以宁愿关闭这个选项。

只是我的想法。如果有人有更多的见解,请发表意见!