Including "standalone" code in compiled queries

What happens behind the curtains when I include a function in my compiled query, for example, I do with DataConvert.ToThema () here to convert the table object to my custom business object:

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; } } 

and name it as follows

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

Apparently, the function is not translated into SQL or something (as it could), but it also fails when I set a breakpoint there in VS2010.

This works without problems, but I don’t understand how and why. What exactly is going on there?

+6
source share
2 answers

The static method of DataConvert.ToThema() is simply to create an instance of a type that has a default constructor and sets various properties, is this correct? If yes, then this is not much different from:

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

Calling Queries.GetThemaByTitle compiles the request. (The way you call it, by the way, may or may not give you any benefits from precompilation). This “query” is actually a tree of code expressions, only part of which is designed to generate SQL code that is sent to the database.

Other parts of it will generate IL code that captures what is returned from the database and puts it in some form for your consumption. LINQ (EF or L2S) is smart enough to be able to take a call to a static method and generate an IL from it to do what you want - and perhaps this is done using an internal delegate or some. But ultimately, it should not be (strongly) different from what would be created from what I replaced above.

But note that this happens no matter what type you return; somewhere, IL code is generated that puts the DB values ​​in the CLR. This is another part of these expression trees.


If you want to take a closer look at these expression trees and what they are involved in, I will have to dig at ya, but I'm not sure about your question if that is what you are looking for.

+2
source

Let me start by saying that if you compile your query or not, it does not matter. You will observe the same results, even if you have not precompiled.

Technically, as Andrew noted, making this work not so difficult. When a LINQ expression is evaluated, an expression tree is created internally. Your function is displayed as a node in this expression tree. There is no magic here. You can write this expression in both L2S and L2E, and it will compile and work fine. This is until you try to actually execute the actual SQL query against the database. This is where the difference begins. L2S seems to happily perform this task, while L2E fails with a NotSupportedException and reports that it does not know how to convert ToThema into a storage request.

So what's going on inside? In L2S, as Andrew explained, the query compiler understands that your function can be run separately from the storage request. Thus, it issues calls to your function in the object read pipeline (where the data read from SQL is converted to objects that are returned as a result of your call).

Once Andrew’s thing wasn’t quite right, what's important is what's inside your static method. I do not think so.

If you put a breakpoint in the debugger for your function, you will see that it is called once for each returned line. In the stack trace, you will see the “Easy Function”, which actually means that the method was emitted at runtime. So this is how it works for Linq to Sql.

The Linq to Entity team seemed to be on a different route. I don’t know what were the reasons why they decided to ban all InvocationExpressions from L2E requests. It may have been performance considerations, or it could be the fact that they need to support all providers, not just SQL Server, so that data readers can behave differently. Or they simply thought that most people do not understand that some of them are executed for the returned row and prefer to close this option.

Just my thoughts. If anyone has a deeper understanding, please call!

+1
source

Source: https://habr.com/ru/post/885421/


All Articles