Passing compiled expressions to where() clause - c#

I'm using calculated properties on my EF object which can't be passed directly to a where() clause:
{"The specified type member 'SomeProp' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported."}
Based on what I read on SO, this could be avoided by passing a Expression<Func<T, bool>> as the argument to where().
Compiling the expression to a Func works fine:
Expression<Func<Foo, bool>> expr = e => f => f.SomeCalculatedProperty == 1;
Func<Foo, bool> compiled = expr.Compile();
Foo result = dbContext.Foo.Where(compiled);
But passing the expression gives me the error above, i.e:
Expression<Func<Foo, bool>> expr = e => f => f.SomeCalculatedProperty == 1;
Foo result = dbContext.Foo.Where(e);
Based on this, shouldn't the last example work fine?

In example one you are calling the extension method Queryable.Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate) this converts the query to sql and applies the filter server side.
In example two you are calling the extension method Enumerable.Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate), this returns all rows from the database then in memory in the program it applies the filter.
For your third example, I will just quote Servy's comment
Your first and third snippets are functionally identical. There must
be some difference that you haven't shown for one to work and another
to not.

Related

Dynamically construct where clause with Func<T, string> lambda - linq to entities

(this is for .Net Framework 4.7)
I'm trying to write up some extension methods to aid in creating dynamic where clauses for various entities. I started a few days ago, so there's likely a lot that I don't know and some that I probably misunderstood.
I managed to create one extension method already for filtering by 1 property which works as I expect it to (I did use reflection to get the property, couldn't get it working with an interface - well, without it executing the sql that is).
I can't seem to be able to get this one working for a lambda expression though.
Note, that the solution must not trigger sql execution. Because I was able to write up some variants that "worK', but they will trigger sql execution.
The way I work with this is that once I have the code ready, I start debugging and have the "query" in the watch. And it looks like this (notice the sql code)
Once I step over my FilterString method call, it either turns into a sql result, or I get an exception (with current code), which it shouldn't:
So here's my current code that throws the exception (currently not dealing with the "match" parameter, I am implementing an "equals" call. There will be others like, starts With, like, etc)
The exception is just one of those "type mismatch" having function cannot be passed as param to string Equals or what not.
public static IQueryable<T> FilterString<T>(this IQueryable<T> query, Match match,
string criteriaItem, Expression<Func<T, string>> getItemString)
where T : class
{
if (string.IsNullOrEmpty(criteriaItem))
{
return query;
}
var param = Expression.Parameter(typeof(T), "r");
var selector = Expression.Lambda<Func<T, string>>(getItemString, param);
Expression<Func<string, bool>> prototype = item => item == criteriaItem;
var predicate = Expression.Lambda<Func<T, bool>>(
prototype.Body.ReplaceParameter(prototype.Parameters[0], selector.Body),
selector.Parameters[0]);
return query.Where(predicate);
}
and the one that executes the sql instead of just generating it
public static IQueryable<T> FilterString<T>(this IQueryable<T> query, Match match,
string criteriaItem, Expression<Func<T, string>> getItemString)
where T : class
{
if (string.IsNullOrEmpty(criteriaItem))
{
return query;
}
var param = Expression.Parameter(typeof(T), "r");
//var value = Expression.Constant(getItemString);
var equals = typeof(string).GetMethod("Equals", new Type[] { typeof(string) });
var item = Expression.Invoke(getItemString, param);
var body = Expression.Call(Expression.Constant(criteriaItem),
equals,
item);
return query.Where(Expression.Lambda<Func<T, bool>>(body, param));
}
calling these is done like so
query = query.FilterString(match, criteria_value, (r) => r.SomeProperty.MaybeSomeOtherProp.SomeString);
query = query.FilterString(match, criteria_value, (r) => r.SomeProperty.Name);
This same extension method will be called on any number of different entities, with nay number of different properties and prop names. I guess I could make use of the reflection version I got working and passing in all the property names in some array of some sort, but that is just plain ugly.
So long story short, how can I get this working in the way I explained above, taht is: having the sql generated instead of executed?
Thank you,
Note, the "ReplaceParameter" extension method is the one from here: https://stackoverflow.com/a/39206392/630515
So, you're trying to merge your prototype item => item == criteriaItem. With a passed in string property expression, like (r) => r.SomeProperty.Name to create (r) => r.SomeProperty.Name == criteriaItem.
Expression<Func<string, bool>> prototype = item => item == criteriaItem;
var predicate = Expression.Lambda<Func<T, bool>>(
ReplacingExpressionVisitor.Replace(
prototype.Parameters[0],
getItemString.Body,
prototype.Body),
getItemString.Parameters[0]);
And I think you're trying to do it this way so that criteriaItem is bound to an sql parameter, rather than being inlined as a string constant. But your question was a little hard to follow.

Logical Inverse of a Func<T, bool>

I have some fairly complex Entity Framework queries throughout my codebase and I decided to centralize the logic into the models. Basically, picture a bunch of controllers with large queries and lots of repeated code in their expression trees. So I took out parts of those expression trees and moved them to the models, allowing for less repetition.
For example, let's say I often need to fetch models called Entity which are in a state of Not Deleted. On my Entity model I have:
public static Func<Entity, bool> IsNotDeleted = e =>
e.Versions != null ?
e.Versions.OrderByDescending(v => v.VersionDate).FirstOrDefault() != null ?
e.Versions.OrderByDescending(v => v.VersionDate).First().ActivityType != VersionActivityType.Delete :
false :
false;
(This is one of the smaller examples, mostly just checking for valid data before trying to examine that data.)
And using it would look like:
var entities = EntityRepository.Entities.Where(Entity.IsNotDeleted).Where(...
I'm finding, however, that while sometimes I want records which are not deleted, other times I want records which are deleted. To do that, is there a way to invert the logic from the consuming code? Something conceptually akin to this (which obviously doesn't work):
var entities = EntityRepository.Entities.Where(!Entity.IsDeleted).Where(...
I'd prefer not to have two Func<>s on the object, one for IsDeleted and one for IsNotDeleted which are nearly identical. The Func<> returns a bool, is there a syntax to call the inverse of it when putting it in a .Where() clause?
Consider the following extension methods.
public static class Functional
{
public static Func<T, bool> Not<T>(this Func<T, bool> f)
{
return x => !f(x);
}
public static Expression<Func<T, bool>> Not<T>(
this Expression<Func<T, bool>> f)
{
// 1. Break the lambda f apart into its parameters and body.
// 2. Wrap the body expression with a unary not expression (!).
// 3. Construct a new lambda with the modified body.
return Expression.Lambda<Func<T, bool>>(
Expression.Not(f.Body), f.Parameters);
}
}
Entity.IsDeleted.Not() is the same as Entity.IsNotDeleted().
Note you probably want to be using Expression<Func<T, bool>> - not Func<T, bool> - so that your lambda logic can be used database-side rather than client-side.
You can use it like this:
Expression<Func<int, bool>> isNegative = x => x < 0;
Expression<Func<int, bool>> isNonNegative = isNegative.Not();
You don't need full blown functional lambda decalration. Go the Church-Turing way - reccursion for the IsNotDeleted definition:
public static Func<Entity, bool> IsNotDeleted = e => !IsDeleted(e);
Answer above is even more "Church-Turing-y" :)

Understanding Expression.Invoke() Method

I've been understanding PredicateBuilder extension methods written Joseph Albahari and I saw this Expression.Invoke and honestly I couldn't understand the reason of it in the following method :
public static Expression<Func<T, bool>> Or<T> (this Expression<Func<T, bool>>
expr1, Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke (expr2,
expr1.Parameters.Cast<Expression> ());
return Expression.Lambda<Func<T, bool>>
(Expression.OrElse (expr1.Body, invokedExpr), expr1.Parameters);
}
Even though he explained it a little bit:
The interesting work takes place inside the And and Or methods. We
start by invoking the second expression with the first expression’s
parameters. An Invoke expression calls another lambda expression using
the given expressions as arguments. We can create the conditional
expression from the body of the first expression and the invoked
version of the second. The final step is to wrap this in a new lambda
expression.
MSDN tells me that:
Creates an InvocationExpression that applies a delegate or lambda
expression to a list of argument expressions.
and this makes sense to me little bit. So basically I don't have to pass in any arguments if I use the expression like that.
But for some reason I couldn't quite understand it. Maybe I am tired or something.
Questions:
When and in which situation does it makes sense to use InvocationExpression.
Can anyone explain how Or<T> method (or AndElse<T>) method works little better?
Update:
I was thinking about InvocationExpression when I was coming from work to home and it hinted in my mind like this:
When we invoke a method, we simple say CallMe(phoneNumber, time); and this is called method invocation. Then, InvocationExpression should be an expression that expresses CallMe(phoneNumber, time);. It is similar to LambdaExpression which express a lambda such as t => t + 2. So basically it is a method invocation that is applied to arguments (not parameters). So as invocation, it is no longer expected to need a parameters but perhaps return something since arguments are already applied to its parameters.
For more information about the code I am talking about, please visit http://www.albahari.com/nutshell/predicatebuilder.aspx
Imagine you weren't working with expressions, but with delegates. Then you could write Or like this:
public static Func<T, bool> Or<T>(this Func<T, bool> expr1, Func<T, bool> expr2)
{
return x => expr1(x) || expr2(x);
}
You create a new delegate that invokes the two delegates, combined using ||. When you rewrite this to use expressions, invoking a delegate turns into Expression.Invoke():
public static Expression<Func<T, bool>> Or<T>(
this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
var parameter = Expression.Parameter(typeof(T), "x");
var invokedExpr1 = Expression.Invoke(expr1, parameter);
var invokedExpr2 = Expression.Invoke(expr2, parameter);
return Expression.Lambda<Func<T, bool>>(
Expression.OrElse(invokedExpr1, invokedExpr2), parameter);
}
The reason why the actual Or isn't written like this is (most likely) an optimization: you don't have to invoke both expressions, you can reuse the body and parameter from one of them. (But you can't reuse both of them, because they have different parameters.)

Linq: Create logical inverse of expression

I would like to create a method that accepts an Expression<Func<T, bool>> and creates the logical inverse of it (i.e. it would return false where it would have returned true, and vice versa. This is much harder than I thought. This is where I am up to:
public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expression)
{
return Expression.Lambda<Func<T, bool>>(Expression.Not(expression.Body));
}
This compiles fine but throws the following Exception when called:
Test method Tests.Common.Unit.LinqPredicateBuilderTests.CanInverseAPredicate threw exception:
System.ArgumentException: Incorrect number of parameters supplied for lambda declaration
I have no idea what I'm doing. Could anyone fill in the blanks?
You're calling Expression.Lambda to create an expression with no parameters at all, when you should be forwarding the single parameter of the source expression.
Note that we are trying to create an Expression<Func<T, bool>> and not an Expression<Func<bool>>.
Try this instead:
return Expression.Lambda<Func<T, bool>>(Expression.Not(expression.Body),
expression.Parameters);

C# LINQ Where Predicate Type Arguments

I have an XElement with values for mock data.
I have an expression to query the xml:
Expression<Func<XElement, bool>> simpleXmlFunction =
b => int.Parse(b.Element("FooId").Value) == 12;
used in:
var simpleXml = xml.Elements("Foo").Where(simpleXmlFunction).First();
The design time error is:
The type arguments for method 'System.Linq.Enumerable.Where(System.Collections.Generic.IEnumerable, System.Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly'
The delegate supplied to Where should take in an XElement and return a bool, marking if the item matches the query, I am not sure how to add anything more to the delegate or the where clause to mark the type.
Also, the parallel method for the real function against the Entity Framework does not have this issue. What is not correct with the LINQ-to-XML version?
Don't make simpleXmlFunction an Expression<Func<XElement, bool>>. Make it a Func<XElement, bool>. That's what's expected as a delegate of .Where.
Func<XElement, bool> simpleXmlFunction =
new Func<XElement, bool>(b => int.Parse(b.Element("FooId").Value) == 12);
I think the full answer includes the previous answer, David Morton's comment, and an updated code snippet:
The .Where implementation for IQueryable is different than the .Where implementation for IEnumerable. IEnumerable.Where expects a:
Func<XElement, bool> predicate
You can compile the function from the expression you have by doing:
Expression<Func<XElement, bool>> simpleXmlExpression =
b => int.Parse(b.Element("FooId").Value) == 12;
Func<XElement, bool> simpleXmlFunction = simpleXmlExpression.Compile();
var simpleXml = xml.Elements("Foo").Where(simpleXmlFunction).First();
This will allow you to look at the expression tree generated and to use the compiled form to query the xml collection.

Categories