linq to entities and store expression - c#

I work on project that use some dynamic linq query to an entities.
i have huge amount of case and to avoid code duplication i refactoring to a method.
But using method which isn't in store expression will result to throw an exception.
One of solutions is to encapsulate method result into an expression which can be interpreted by linq to entitie query.
Consider that code :
parentExpression = x => x.child.Any(y=>IsGoodChild(y,childType, childSize));
private bool IsGoodChild(child c, int childType, int childSize){
return c.type == childType && c.size == childSize;
}
"parentExpression " is predicate of type "Parent" of my EF.
This code throw an exception, "IsGoodChild" method return a boolean and can't be interpreted by linq to Entities.
So, i would like something like this :
parentExpression = x => x.child.AsQueryable().Any(IsGoodChild(childType, childSize));
private System.Linq.Expression.Expression<Func<child, bool>> IsGoodChild(int childType, int childSize){
return ????
}
So how can i do "IsGoodChild(...)" can work even if which not take x.child attribute ?
Thx for advance
Re,
I try something, when i write lambda directly in expression like this :
parentExpression = x => x.child.Any(y=>y.type == childType && y.size == childSize);
i used extract method from resharper and generate it this :
private Expression<Func<child,Boolean>> IsGoodChildFunctional(Int32 childType, Int32 childSize)
{
return c => c.type == childType && c.size == childSize;
}
But i also have .NET Framework Data Provider error 1025' error ...

In this case the compiler is clever, given an anonymous method it will switch between an expression tree or a compiled lambda depending on the declared type. The following should work:
private Expression<Func<child,Boolean>>
IsGoodChildFunctional(Int32 childType, Int32 childSize)
{
return c => c.type == childType && c.size == childSize;
}
which would be used like so:
parentExpression = x => x.child
.AsQueryable()
.Any(IsGoodChildFunctional(childType,childSize));

Create a static generic method which will return an Expression. The Expression is built by using factory methods.
public static Expression<Func<TTargetObject,Boolean>> IsGoodChildFunctional<TTargetObject>(Int32 childType, Int32 childSize)
{
var e = Expression.Parameter(typeof(TTargetObject), "e");
var childTypeMember = Expression.MakeMemberAccess(e, typeof(TTargetObject).GetProperty("childType"));
var childSizeMember = Expression.MakeMemberAccess(e, typeof(TTargetObject).GetProperty("childSize"));
var childTypeConstant = Expression.Constant(childType, childType.GetType());
var childSizeConstant = Expression.Constant(childSize, childSize.GetType());
BinaryExpression b;
BinaryExpression bBis;
Expression<Func<TTargetObject, bool>> returnedExpression;
b = Expression.Equal(childTypeMember , childTypeConstant );
bBis2 = Expression.Equal(childSizeMember, c2);
var resultExpression = Expression.AndAlso(b, bBis);
returnedExpression = Expression.Lambda<Func<TTargetObject, bool>>(resultExpression , e);
return returnedExpression;
}
It is called like this:
var predicat = IsGoodChildFunctional<child>(childType, childSize);
parentExpression = x => x.child.Any(predicat);

Related

How to generalize a part of a LINQ to SQL query

I have the following snippet that is repeated through my code for different fields:
if (filters.NameIsLike)
query = query.Where (x => EF.Functions.Like (x.Name, $"%{filters.Name}%"));
else
query = query.Where (x => x.Name.ToLower () == filters.Name.ToLower ());
How can I create an extension method that generalizes what the snippet does?
Something like:
public static IQueryable<T> EqualsOrLike (this IQueryable<T> query, ??? field, bool isLike, string filter)
{
if (isLike)
return query.Where (x => EF.Functions.Like (field.ToLower (), $"%{filter.ToLower ()}%"));
else
return query.Where (x => field.ToLower () == filter.ToLower ());
}
Thanks in advance!
UPDATE:
This is how the initial snippet is used (as an example):
In this case the class is Company, but it could be something different... Therefore I don't know the class nor the field.
And the comment explain how I would use the EqualsOrLike function:
protected override IQueryable<Company> _QueryAsync (IQueryable<Company> query, QueryFilterBase filter)
{
CompanyQueryFilter filters = (CompanyQueryFilter)filter;
if (!string.IsNullOrEmpty (filters.Name))
{
if (filters.NameIsLike)
query = query.Where (x => EF.Functions.Like (x.Name.ToLower (), $"%{filters.Name.ToLower ()}%"));
else
query = query.Where (x => x.Name.ToLower () == filters.Name.ToLower ());
//query = EqualsOrLike (query, x => x.Name, filters.NameIsLike, filters.Name);
}
if (!string.IsNullOrEmpty (filters.AtecoCode))
query = query.Where (x => EF.Functions.Like (x.AtecoCode, $"%{filters.AtecoCode}%"));
if (!string.IsNullOrEmpty (filters.VatNumber))
{
if (filters.VatNumberIsLike)
query = query.Where (x => EF.Functions.Like (x.VatNumber, $"%{filters.VatNumber}%"));
else
query = query.Where (x => x.VatNumber.ToLower () == filters.VatNumber.ToLower ());
//query = EqualsOrLike (query, x => x.VatNumber, filters.VatNumberIsLike, filters.VatNumber);
}
return query;
}
Since you don't know the property to compare, you need to build your expression by hand.
Option 1, you can construct the entire expression explicitly. As a first step, I find it useful to see how C# compiles an expression by using a decompiler;
Expression<Func<C,bool>> expr = x => x.Name.ToLower () == filter;
Cleaning that up a little would give you the following;
private class LambdaCaptures
{
public string filter;
}
var locals = new LambdaCaptures();
locals.filter = "";
ParameterExpression parameterExpression =
Expression.Parameter(typeof(C), "x");
var expr =
Expression.Lambda<Func<C, bool>>(
Expression.Equal(
Expression.Call(
Expression.MakeMemberAccess(
parameterExpression,
typeof(C).GetProperty(nameof(C.Name))
),
typeof(string).GetMethod(nameof(string.ToLower), new Type[] { }),
Array.Empty<Expression>()
),
Expression.Field(
Expression.Constant(locals, typeof(LambdaCaptures)),
typeof(C).GetField(nameof(LambdaCaptures.filter))
)
),
parameterExpression);
Which you can now tweak to replace the incoming type and property name.
Note that you'll want to keep the captured lambda so that EF binds this value as a parameter. Otherwise EF and Sql server will need to recompile the query every time.
Option 2, you can use an ExpressionVisitor to tweak a template expression. For example, if you had an expression for both the field you wish to compare, and the comparison you wish to perform. You can replace the parameter from one expression with the body of the other.
Giving a complete solution that would look something like;
public static Expression<Func<T, bool>> BuildExpr<T, V>(Expression<Func<V, bool>> operation, Expression<Func<T, V>> value)
=> Expression.Lambda<Func<T, bool>>(
ReplacingExpressionVisitor.Replace(
operation.Parameters.Single(),
value.Body,
operation.Body
),
value.Parameters.Single());
public static IQueryable<T> EqualsOrLike<T>(this IQueryable<T> query, Expression<Func<T, string>> value, bool isLike, string filter)
{
if (string.IsNullOrEmpty(filter))
return query;
if (isLike)
{
filter = $"%{filter.ToLower()}%";
var expr = BuildExpr(x => EF.Functions.Like(x, filter), value);
return query.Where(expr);
}
else
{
filter = filter.ToLower();
var expr = BuildExpr(x => x.ToLower() == filter, value);
return query.Where(expr);
}
}
query = query.EqualsOrLike(d => d.Name, filters.NameIsLike, filters.NameValue);
Since this type of replacement is a fairly common operation, you can re-use EF Core's ReplacingExpressionVisitor instead of writing your own.
You can use a Func to select the field from the result:
public static IQueryable<T> EqualsOrLike<T>(this IQueryable<T> query, Func<T, string> fieldSelector, bool isLike, string filter)
{
if (isLike)
return query.Where(x => EF.Functions.Like(fieldSelector(x).ToLower(), $"%{filter.ToLower ()}%"));
else
return query.Where(x => fieldSelector(x).ToLower() == filter.ToLower ());
}
You would then call it like so:
var results = someQueryable.EqualsOrLike(x => x.Name, false, "Robert");

Output Expression Value constant-like

I have to send expressions over http to my backend. This backend knows about enum Fun but doesn't have a reference to funs.
My job is to serialize exp2 in a way the backend can still deserialize it
Is there a way to force passing the enum value rather than a reference to the array element?
var funs = new[] { Fun.Low, Fun.High };
Expression<Func<Funky, bool>> exp1 = x => x.Status == Fun.Low;
Expression<Func<Funky, bool>> exp2 = x => x.Status == funs[0];
Console.WriteLine(exp1);
//Expected: x => (Convert(x.Status, Int32) == 1)
Console.WriteLine(exp2);
//Actual output: x => (Convert(x.Status, Int32) == Convert(value(Program+<>c__DisplayClass0_0).funs[0], Int32))
public enum Fun : int {
Low = 1,
Middle = 2,
High = 420
}
public class Funky {
public Fun Status {get;set;} = Fun.High;
}
Question: how can I make exp2 the same result as exp1?
_____________________________________________
Background Info:
exp1 serializes the enum value as 1 which can be correctly interpreted by the backend.
exp2 serializes funs[0] as a reference to the actual array-element, looking like this: Convert(value(Program+<>c__DisplayClass0_0).funs[0], Int32)
I also tried exp3 but this outputs the value still as a reference rather than the constant enum value.
What I've tried so far:
//another tests
var afun = new Funky();
var param = Expression.Parameter(typeof(Funky), "x");
var key = afun.GetType().GetProperty("Status");
var lhs = Expression.MakeMemberAccess(param, key);
var rhs = Expression.ArrayIndex(Expression.Constant(funs), Expression.Constant(0));
var body = Expression.Equal(lhs, rhs);
var exp3 = Expression.Lambda<Func<Funky, bool>>(body, param);
Console.WriteLine(exp3);
//x => (x.Status == value(Fun[])[0])
Real-life example:
The Backend holds a database that will be queried via EF-LINQ.
The Frontend is supposed to send the exact LINQ Query to the backend.
Lets say a User of the Frontend has a checklist, through which he can toggle which Funky objects he can query from Backend:
[x] Low
[x] Middle
[_] High
-> outputs var funs = new[] { Fun.Low, Fun.Middle };
Now the Frontend will have to put the Expression together like so:
Expression<Func<Funky, bool>> exp2 = x => x.Status == funs[0] || x.Status == funs[1];
and serialize it before it sends it to the backend.
The backend wont be able to understand funs[0] or funs[1]. But Backend knows about enum Fun and could deserialize 1 and 2 correctly.
Basically, you need to rewrite the Expression to remove all the indirection and use the literal value directly. This can be done with an ExpressionVisitor - a simplified example is shown below (it handles your scenario) - but if you want to handle more complex things like method invocations (evaluated locally), you'll need to add more override methods:
public class SimplifyingVisitor : ExpressionVisitor
{
protected override Expression VisitBinary(BinaryExpression node)
{
if (node.NodeType == ExpressionType.ArrayIndex)
{
if (Visit(node.Left) is ConstantExpression left
&& left.Value is Array arr && arr.Rank == 1
&& Visit(node.Right) is ConstantExpression right)
{
var type = left.Type.GetElementType();
switch (right.Value)
{
case int i:
return Expression.Constant(arr.GetValue(i), type);
case long l:
return Expression.Constant(arr.GetValue(l), type);
}
}
}
return base.VisitBinary(node);
}
protected override Expression VisitUnary(UnaryExpression node)
{
if (node.NodeType == ExpressionType.Convert
&& Visit(node.Operand) is ConstantExpression arg)
{
try
{
return Expression.Constant(
Convert.ChangeType(arg.Value, node.Type), node.Type);
}
catch { } //best efforts
}
return base.VisitUnary(node);
}
protected override Expression VisitMember(MemberExpression node)
{
if (node.NodeType == ExpressionType.MemberAccess && Visit(node.Expression) is ConstantExpression target)
{
switch (node.Member)
{
case PropertyInfo property:
return Expression.Constant(property.GetValue(target.Value), property.PropertyType);
case FieldInfo field:
return Expression.Constant(field.GetValue(target.Value), field.FieldType);
}
}
return base.VisitMember(node);
}
}
usage:
var visitor = new SimplifyingVisitor();
exp2 = (Expression<Func<Funky, bool>>)visitor.Visit(exp2);

How to make LINQ-to-Objects handle projections?

I have implemented a basic (naive?) LINQ provider that works ok for my purposes, but there's a number of quirks I'd like to address, but I'm not sure how. For example:
// performing projection with Linq-to-Objects, since Linq-to-Sage won't handle this:
var vendorCodes = context.Vendors.ToList().Select(e => e.Key);
My IQueryProvider implementation had a CreateQuery<TResult> implementation looking like this:
public IQueryable<TResult> CreateQuery<TResult>(Expression expression)
{
return (IQueryable<TResult>)Activator
.CreateInstance(typeof(ViewSet<>)
.MakeGenericType(elementType), _view, this, expression, _context);
}
Obviously this chokes when the Expression is a MethodCallExpression and TResult is a string, so I figured I'd execute the darn thing:
public IQueryable<TResult> CreateQuery<TResult>(Expression expression)
{
var elementType = TypeSystem.GetElementType(expression.Type);
if (elementType == typeof(EntityBase))
{
Debug.Assert(elementType == typeof(TResult));
return (IQueryable<TResult>)Activator.CreateInstance(typeof(ViewSet<>).MakeGenericType(elementType), _view, this, expression, _context);
}
var methodCallExpression = expression as MethodCallExpression;
if(methodCallExpression != null && methodCallExpression.Method.Name == "Select")
{
return (IQueryable<TResult>)Execute(methodCallExpression);
}
throw new NotSupportedException(string.Format("Expression '{0}' is not supported by this provider.", expression));
}
So when I run var vendorCodes = context.Vendors.Select(e => e.Key); I end up in my private static object Execute<T>(Expression,ViewSet<T>) overload, which switches on the innermost filter expression's method name and makes the actual calls in the underlying API.
Now, in this case I'm passing the Select method call expression, so the filter expression is null and my switch block gets skipped - which is fine - where I'm stuck at is here:
var method = expression as MethodCallExpression;
if (method != null && method.Method.Name == "Select")
{
// handle projections
var returnType = method.Type.GenericTypeArguments[0];
var expType = typeof (Func<,>).MakeGenericType(typeof (T), returnType);
var body = method.Arguments[1] as Expression<Func<T,object>>;
if (body != null)
{
// body is null here because it should be as Expression<Func<T,expType>>
var compiled = body.Compile();
return viewSet.Select(string.Empty).AsEnumerable().Select(compiled);
}
}
What do I need to do to my MethodCallExpression in order to be able to pass it to LINQ-to-Objects' Select method? Am I even approaching this correctly?
(credits to Sergey Litvinov)
Here's the code that worked:
var method = expression as MethodCallExpression;
if (method != null && method.Method.Name == "Select")
{
// handle projections
var lambda = ((UnaryExpression)method.Arguments[1]).Operand as LambdaExpression;
if (lambda != null)
{
var returnType = lambda.ReturnType;
var selectMethod = typeof(Queryable).GetMethods().First(m => m.Name == "Select");
var typedGeneric = selectMethod.MakeGenericMethod(typeof(T), returnType);
var result = typedGeneric.Invoke(null, new object[] { viewSet.ToList().AsQueryable(), lambda }) as IEnumerable;
return result;
}
}
Now this:
var vendorCodes = context.Vendors.ToList().Select(e => e.Key);
Can look like this:
var vendorCodes = context.Vendors.Select(e => e.Key);
And you could even do this:
var vendors = context.Vendors.Select(e => new { e.Key, e.Name });
The key was to fetch the Select method straight from the Queryable type, make it a generic method using the lambda's returnType, and then invoke it off viewSet.ToList().AsQueryable().

How to Refactor LINQ Query to Use Delegate or Expression<Func<T, bool>>

This method works great and generates only 1 query (utilizing .Includes() to eager load).
However, I would like to refactor the AbleToDelete code to be more reusable.
(Using EF4)
public override IEnumerable<AffectProjection> SelectAll(SearchAffects page)
{
IQueryable<Affect> query = BuildSearchQuery(page);
IEnumerable<AffectProjection> results = query.Select(entity => new AffectProjection()
{
AffectID = entity.AffectID,
AffectCode = entity.AffectCode,
AffectName = entity.AffectName,
AbleToDelete = !((entity.Foo1s.Any(pa => !pa.Inactive))
|| (entity.Foo2s.Any(ea => !ea.Inactive))
|| (entity.Foo3s.Any(sa => !sa.Inactive))
|| (entity.Foo4s.Any(spa => !spa.Inactive)))
}).ToArray();
return results;
}
I moved the code into an Expression Func structure but can't figure out how to replace my code that is setting the AbleToDelete property. Advise?
public Expression<Func<Affect, bool>> DelegateExpression = entity =>
!((entity.Foo1s.Any(pa => !pa.Inactive))
|| (entity.Foo2s.Any(ea => !ea.Inactive))
|| (entity.Foo3s.Any(sa => !sa.Inactive))
|| (entity.Foo4s.Any(spa => !spa.Inactive)));
Last but not least, this solution compiles but fails at runtime with error: "NotSupportedException - The LINQ expression node type 'Invoke' is not supported in LINQ to Entities." Trying to use a delegate which isn't supported:
public override IEnumerable<AffectProjection> SelectAll(SearchAffects page)
{
IQueryable<Affect> query = BuildSearchQuery(page);
//Create delegate instance and register method -- single statement
var deleteAbilityAllowed = new CanDelete(GetAbleToDelete);
IEnumerable<AffectProjection> results = query.Select(entity => new AffectProjection()
{
AffectID = entity.AffectID,
AffectCode = entity.AffectCode,
AffectName = entity.AffectName,
AbleToDelete = deleteAbilityAllowed(entity)
}).ToArray();
return results;
}
public delegate bool CanDelete(Affect entity);
public bool GetAbleToDelete(Affect entity)
{
return !((entity.Foo1s.Any(pa => !pa.Inactive))
|| (entity.Foo2s.Any(ea => !ea.Inactive))
|| (entity.Foo3s.Any(sa => !sa.Inactive))
|| (entity.Foo4s.Any(spa => !spa.Inactive)));
}
Please advise and thank you in advance!
LINQKit contains methods that allow you to do this. With it, you can use yout DelegateExpression. To do that, add AsExpandable() to the source of your query and then invoke the Expression using Invoke():
var expression = DelegateExpression;
var results = query.AsExpandable().Select(entity => new AffectProjection()
{
AffectID = entity.AffectID,
AffectCode = entity.AffectCode,
AffectName = entity.AffectName,
AbleToDelete = expression.Invoke(entity)
}).ToArray();
Be careful though, it seems you can't use the expression directly from the field, it has to be from a local variable.

How to make a compound "or" clause in Linq?

If you're adding "and" conditions to a Linq query, it's easy to do it like so:
var q = MyTable;
if (condition1)
q = q.Where(t => t.Field1 == value1);
if (condition2)
q = q.Where(t => t.Field2 > t.Field3);
// etc.
Is there any clever way of doing the same thing, when you want to add "or" conditions?
You can use a PredicateBuilder and use it to build an Or based expression:
var predicate = PredicateBuilder.False<Product>();
predicate = predicate.Or (t => t.Field1 == value1);
predicate = predicate.Or (t => t.Field2 > t.Field3);
q = q.Where (predicate);
You can read more about it here:
http://www.albahari.com/nutshell/predicatebuilder.aspx
Replace the Product in PredicateBuilder.False<Product>() with your queried object.
Note that you start from a False predicate as you want to use Or. If You'd want an And predicate, Yuo should start from a True
Use the following:
var q = MyTable;
q = q.Where(
t => (condition1 && t.Field1 == value1) || (condition2 && t.Field2 > t.Field3));
var q = MyTable;
var conditions = new List<Func<T, bool>>();
if (condition1)
conditions.Add(t => ...);
if (condition2)
conditions.Add(t => ...);
q.Where(x => conditions.Any(y => y(x)));
There is one way to do this that involves using expression trees. This way you build the Boolean expression yourself. It's pretty straight forward the tricky part though is that you need to rebase the parameters because otherwise it is going to refer the original lambda expression. See below for an example:
static void Main(string[] args)
{
var source = new List<int> { 1, 2, 3 };
var any = new List<Expression<Func<int, bool>>>();
any.Add(x => x == 1);
any.Add(x => x == 3);
foreach (var item in source.AsQueryable().WhereDisjunction(any))
{
Console.WriteLine(item);
}
}
class RewriteSingleParameterUsage : ExpressionVisitor
{
public ParameterExpression Parameter { get; set; }
protected override Expression VisitParameter(ParameterExpression node)
{
return Parameter;
}
}
public static IQueryable<T> WhereDisjunction<T>(this IQueryable<T> source, IList<Expression<Func<T, bool>>> any)
{
switch (any.Count)
{
case 0: return source;
case 1: return source.Where(any[0]);
default:
var p = Expression.Parameter(any[0].Parameters[0].Type, any[0].Parameters[0].Name);
var rw = new RewriteSingleParameterUsage { Parameter = p };
var expr = rw.Visit(any[0].Body);
for (int i = 1; i < any.Count; i++)
{
expr = Expression.Or(expr, rw.Visit(any[i].Body));
}
return source.Where(Expression.Lambda<Func<T, bool>>(expr, p));
}
}
In the above example I'm being very harsh, I'm effectively replacing any parameter with this single new parameter that is being used to create the new expression. However, given the signature of this extension method it shouldn't really be possible to call this method with parameters such that it would cause an error. It is however going to be a problem if you involve more than one parameter.
This is the same answer I gave here
As Marc Gravell said it involves expression-tree combining.
This article shows you how to do that. It takes a bit of work to initially set it up. But its worth it.
Alternate solution is to use Predicate Builder. The article does not explain very well what is actually happening under-the-hood. But the above article explains it nicely

Categories

Resources