Compiling Linq SQL queries from non-trivial IQueryable

Is there a way to use the CompiledQuery.Compile method to compile an expression related to IQueryable? I currently have an IQueryable with a very large expression tree. IQueryable was created using several methods that each component supplied. For example, two methods can return IQueryables, which are then joined in a third. For this reason, I cannot explicitly define the full expression in the call to the compile () method.

I was hoping to pass the expression to the compilation method as someIQueryable.Expression, however this expression is not in the form required by the compilation method. If I try to get around this by putting the request directly into the compilation method, for example:

var foo = CompiledQuery.Compile<DataContext, IQueryable<User>>(dc => dc.getUsers()); var bar = foo(this); 

where I make a call form in a datacontext, I get an error that "getUsers does not appear as a stored procedure or user-defined function". Again, I cannot just copy the contents of the getUsers method to where I make the compilation call, because it, in turn, uses other methods.

Is there a way to pass an expression on an IQueryable returned from getUsers to a compilation method?

Update I tried to force my will on a system with the following code:

  var phony = Expression.Lambda<Func<DataContext, IQueryable<User>>>( getUsers().Expression, Expression.Parameter(typeof(DataContext), "dc")); Func<DataContext, IQueryable<User>> wishful = CompiledQuery.Compile<DataContext, IQueryable<User>>(phony); var foo = wishful(this); 

foo ends:

{System.Data.Linq.SqlClient.SqlProvider + OneTimeEnumerable`1 [Model.Entities.User]}

I don’t have the opportunity to see the results in foo, because instead of offering to expand the results and run the query, I see only the message “Operation can destabilize the execution time”.

I just need to find a way for the sql string to be generated only once and used as a parameterized command for subsequent queries, I can do this manually using the GetCommand method in the data context, but then I must explicitly set all the parameters and make the object itself, which represents several hundred lines of code, given the complexity of this particular request.

Update

John Rusk provided the most useful information, so I awarded him victory on this. However, it took some extra tweaking and there were a couple of other problems that I encountered along the way, so I thought I had to “deploy” the answer. Firstly, the error “Operation can destabilize runtime” was not related to compiling the expression, it was actually due to some casting in the expression tree. In some places, I needed to call the .Cast<T>() method to formally cast objects, even if they were of the correct type. Without going into details, this was mainly required when several expressions were combined into one tree, and each branch could return a different type, each of which was a subtype of the general class.

After solving the problem of debabilization, I returned to the compilation problem. John expand's solution was almost there. He looked for method invocation expressions in the tree and tried to resolve them for the main expression that the method usually returned. My references to expressions were not provided by method calls, but by properties. Therefore, I needed to change the visitor of the expression that performs the extension to include these types:

 protected override Expression VisitMemberAccess(MemberExpression m) { if(m.Method.DeclaringType == typeof(ExpressionExtensions)) { return new ExpressionExpander().Visit((Expression)(((System.Reflection.PropertyInfo)m.Member).GetValue(null, null))); } return base.VisitMemberAccess(m); } 

This method may not be acceptable in all cases, but it should help anyone who finds themselves in the same predicament.

+6
performance linq-to-sql expression-trees
source share
1 answer

Something like this works, at least in my tests:

 Expression<Func<DataContext, IQueryable<User>> queryableExpression = GetUsers(); var expressionWithSomeAddedStuff = (DataContext dc) => from u in queryableExpression.Invoke(dc) where ....; var expressionThatCanBeCompiled = expressionWithSomeAddedStuff.Expand(); var foo = CompiledQuery.Compile<DataContext, IQueryable<User>>(expressionThatCanBeCompiled); 

It looks a bit verbose, and there are probably improvements you can make.

They indicate that it uses the Invoke and Expand methods from LinqKit. They basically allow you to create a query using composition, and then compile the finished result.

+2
source share

All Articles