I'm trying to achieve this kind of expression: "A => A.B.Where(extExp).Count() > 0" and I've got problem with how to make expression for Where(...) which is as I assume extension method for ICollection<>. Could someone help?
Expression<Func<N, bool>> conditions = c => c.T_ID == 1 || c.T_ID == 2;
ParameterExpression mpe = Expression.Parameter(typeof(T), "A");
Expression prop = Expression.Property(mpe,typeof(T).GetProperty("B"));
...
var propWhere = Expression.Call(..., prop, conditions);
How to invoke it properly
What goes there is an overload that of call that takes a MethodInfo. To get the method info I think it is best to use the code from this answer - https://stackoverflow.com/a/21060046/122507
public static MethodInfo GetMethodInfo(Expression<Action> expression)
{
var member = expression.Body as MethodCallExpression;
if (member != null)
return member.Method;
throw new ArgumentException("Expression is not a method", "expression");
}
usage
var whereMethodInfo = GetMethodInfo(() => Enumerable.Where(Enumerable.Empty<T>(), i=>true));
BTW I suggest that you download LINQPad and use it to write queries and look at the generated expression trees and IL code.
Related
A C# lambda expression (of type System.Linq.Expressions.Expression<TDelegate>) in code:
Expression<Func<Something, bool>> predicate = s => s.SomeProperty == 12;
To create a similar instance of System.Linq.Expressions.Expression:
var parameter = Expression.Parameter(typeof(Something), "s");
var property = Expression.Property(parameter, typeof(Something).GetProperty("SomeProperty"));
var constant = Expression.Constant(12);
var expression = Expression.Equal(property, constant);
Is there a way to declare expression given only the predicate? So without building the expression tree step by step in code, but having the compiler infer it from a lambda expression.
var expression = Expression.FromLambda<Something>(s => s.SomeProperty == 12);
Sure, just grab the body of the lambda expression, like so:
Expression FromLambda(Expression<Func<Something, bool>> lambda)
{
return lambda.Body;
}
Then you can use it like so:
var expression = FromLambda(s => s.SomeProperty == 12);
Just return the expression
Expression<Func<T, bool>> FromLambda<T>(Expression<Func<T, bool>> lambda) {
return lambda;
}
And use as desired
var expression = FromLambda<Something>(s => s.SomeProperty == 12);
This however is not very flexible and targets just this scenario. You would need to create methods for any other delegate you want to use.
i try to query (linq to entities EF Core) a navigation properties collection, so i use any() like this :
var query = context.MyTable.Where(x => x.mycollectionproperties.Any(p => p.myprop == myvar );
It's work perfectly but now i want to construct the predicate and not defined it directly in the query.
so i do :
Func<T, bool> mypredicate = (p => p.myprop == myvar);
var query = context.MyTable.Where(x => x.mycollectionproperties.Any(mypredicate);
(I have replace T by my entity name)
but this generate an error : Object of type 'System.Linq.Expressions.TypedParameterExpression' cannot be converted to type 'System.Linq.Expressions.LambdaExpression'.
How can i construct my predicate to use it on Any() collection ?
Thank's
This line for example:
var query = context.MyTable.Where(x => x.mycollectionproperties.Any(p => p.myprop == 1));
When compiled will be compiled to something like this:
var xParameter = Expression.Parameter(typeof(Entity1), "x");
var pParameter = Expression.Parameter(typeof(Entity2), "p");
var anyMethod =
typeof(Enumerable)
.GetMethods()
.Single(x => x.Name == "Any" && x.GetParameters().Length == 2)
.MakeGenericMethod(typeof(Entity2));
var anyCondition = Expression.Lambda<Func<Entity2, bool>>(
Expression.Equal(
Expression.Property(
pParameter,
typeof(Entity2).GetProperty("myprop").GetMethod),
Expression.Constant(1, typeof(int))),
pParameter);
var query = context.MyTable.Where(
Expression.Lambda<Func<Entity1, bool>>(
Expression.Call(
null,
anyMethod,
new Expression[] {
Expression.Property(
xParameter,
typeof(Entity1).GetProperty("mycollectionproperties").GetMethod),
anyCondition
}),
xParameter));
This is called an expression tree. See this reference for more details:
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expression-trees/
Although the Any method takes a Func, when constructing the expression tree, notice that an expression (Expression<Func<Entity2, bool>>) is given to the Any method.
There doesn't seem to be a way from C# to give the Any method an expression instead of a Func even if the whole thing is an expression tree (I mean in a parameterized way like you want to achieve).
The most obvious way to achieve what you want is to use the code from this post and replace the anyCondition variable with whatever expression you want to use for the condition inside Any.
Another way is to construct part of the expression tree "normally" and pass null to the Any method and then use an expression visitor to replace the null with your expression. Here is how such visitor would look like:
public class AnyMethodArgumentReplacingVisitor : ExpressionVisitor
{
private readonly Expression expression;
public AnyMethodArgumentReplacingVisitor(Expression expression)
{
this.expression = expression;
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method.Name == "Any")
{
return Expression.Call(node.Object, node.Method, node.Arguments[0], expression);
}
return base.VisitMethodCall(node);
}
}
Here is how you would use it:
Expression<Func<Entity2, bool>> predicate =
a => a.myprop == 2;
Expression<Func<Entity1, bool>> expression =
b => b.mycollectionproperties.Any(null);
var expression2 =
(Expression<Func<Entity1, bool>>)
new AnyMethodArgumentReplacingVisitor(predicate).Visit(expression);
Please note that such visitor would replace the call to any Any method. It also assumes that only the overload of Any that takes a predicate is used. There is another overload of Any that does not take a predicate. If you need to use that, you need to adjust the code.
It looks to me your problem is in your definition of
Func<T, bool> mypredicate = (p => p.myprop == myvar);
You should not use T, you should use the type of mycollectionproperties
Assuming the property mycollectionproperties is defined as something like this
....
public IQueryable<YourType> mycollectionproperties { get; set; }
....
Then you should declare mypredicate as
Func<YourType, bool> mypredicate = (p => p.myprop == myvar);
You can see a working sample on .NetFiddle
I have a method that takes IQueryable<T> and I want to implement OrderBy generically inside it. Ideally by passing in c => c.SomeProperty in as a parameter but I can't figure out how to get the generics working on that so I've tried it with a string. However I am getting the error:
Incorrect number of parameters supplied for lambda declaration
This is what I tried (using the string method)
var sortSelectorParameter = Expression.Parameter(typeof(T), "c");
var sortSelector = Expression.PropertyOrField(sortSelectorParameter, "ClientId"); // ClientId is the property string
collection = collection.OrderByDescending(Expression.Lambda<Func<T, bool>>(sortSelector));
I am very confused as OrderBy only takes one parameter - any advice?
You need to pass the parameter to Expression::Lambda<T>, as the error says:
var sortSelectorParameter = Expression.Parameter(typeof(T), "c");
var sortSelector = Expression.PropertyOrField(sortSelectorParameter, "ClientId"); // ClientId is the property string
collection = collection.OrderByDescending(Expression.Lambda<Func<T, bool>>(sortSelector, sortSelectorParameter ));
Your "body" for the lambda refers to a parameter c, which is represented by the ExpressionParameter instance sortSelectorParameter. You need to pass this parameter instance to the lambda, so that it knows that the parameter referred to by the body is in fact the in-parameter of the lambda you wish to create.
EDIT: The above may answer your technical question, but it is unclear what you are trying to achieve here. If you just want to order by something which you know at compile-time, then you don't need any of this. What is the point of wrapping the OrderByDescending-method?
IQueryable<TElement> MySpecialOrderBy<TElement, TKey>(IQueryable<TElement> source, Expression<Func<TElement, TKey>> keySelector)
{
return source.OrderByDescending(keySelector);
}
It is quite complex:
private static readonly MethodInfo OrderByDescending = (from x in typeof(Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public)
where x.Name == "OrderByDescending"
let args = x.GetGenericArguments()
where args.Length == 2
let pars = x.GetParameters()
where pars.Length == 2 &&
pars[0].ParameterType == typeof(IQueryable<>).MakeGenericType(args[0]) &&
pars[1].ParameterType == typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(args))
select x).Single();
public static IQueryable<T> OrderByStringDescending<T>(this IQueryable<T> query, string parameter)
{
var par = Expression.Parameter(typeof(T), "obj");
var selector = Expression.PropertyOrField(par, parameter);
var lambda = Expression.Lambda(selector, par);
return (IQueryable<T>)OrderByDescending.MakeGenericMethod(typeof(T), selector.Type).Invoke(null, new object[] { query, lambda });
}
You have to build an Expression.Lambda. The problem is that invoking the Queryable.OrderByDescending (that has two parameters, the IQueryable<> query and the Expression<>) without knowing the TKey type at compile time is complex. I solve it through reflection.
If all you need is to pass an expression the some method that does other stuff with the collection then it's easy:
public void Test<T,TKey>(IQueryable<T> collection, Expression<Func<T,TKey>> orderByExpr)
{
// your logic here
// ...
collection = collection.OrderByDescending(orderByExpr);
}
And you call it like this:
this.Test(collection, c => c.ClientId);
I am a novice at Linq and a true beginner with expression trees.
I have a generic expression routine that builds a simple Linq where clause that I found at:
https://www.simple-talk.com/dotnet/net-framework/dynamic-linq-queries-with-expression-trees/
public Func<TSource,bool> SimpleFilter<TSource> (string property, object value)
{
var type = typeof(TSource);
var pe = Expression.Parameter(type, "p");
var propertyReference = Expression.Property(pe,property);
var constantReference = Expression.Constant(value);
var ret = Expression.Lambda<Func<TSource, bool>>
(Expression.Equal(propertyReference, constantReference), new[] { pe });
return ret.Compile();
}
When I call the function as SimpleFilter("JobCustomerID", 449152) it
yields (p => p.JobCustomerId == 449152) which is correct.
If I manually place that criteria in my Linq statement, I get the correct return.
var jj = db.VW_Job_List.Where((p => p.JobCustomerId == 449152));
However when called via the filter function, the Linq throws an OutOfMemoryException.
It is called in my application as:
var jj = db.VW_Job_List.Where(SimpleFilter<VW_Job_List>("JobCustomerID", 449152));
If I call the function with a text criterion, it returns properly:
var jj = db.VW_Job_List.Where(SimpleFilter<VW_Job_List>("CompanyCode", "LCS"));
Is there something specific about using an integer variable that needs to be accommodated? Do I have something coded incorrectly? Any thoughts or insights will be appreciated.
The two calls
var jj = db.VW_Job_List.Where((p => p.JobCustomerId == 449152));
and
var jj = db.VW_Job_List.Where(SimpleFilter<VW_Job_List>("JobCustomerID", 449152));
are not equivalent. The first resolves to Queryable.Where, hence the filter is applied inside the database, while the second - to Enumerable.Where, thus causing loading the whole table in memory and applying the filter there.
The problem is that the return type of your SimpleFilter is Func<TSource, bool>. In order to make them equivalent, it should be Expression<Func<TSource, bool>>. Note that although they look visually the same, there is a huge difference between lambda expression and lambda delegate due to the different overload resolution when applied to IQueryable<T>.
So, change the method like this and try again:
public Expression<Func<TSource,bool>> SimpleFilter<TSource> (string property, object value)
{
var type = typeof(TSource);
var pe = Expression.Parameter(type, "p");
var propertyReference = Expression.Property(pe,property);
var constantReference = Expression.Constant(value);
var ret = Expression.Lambda<Func<TSource, bool>>
(Expression.Equal(propertyReference, constantReference), new[] { pe });
return ret; // No .Compile()
}
Problem
I'm working on refactoring some LINQ queries for several reports in our web application, and I'm attempting to move some duplicate query predicates into their own IQueryable exension methods so we can reuse them for these reports, and reports in the future. As you can probably infer, I've already refactored the predicate for groups, but the predicate for codes is giving me problems. This is an example of one of the report methods I have so far:
DAL method:
public List<Entities.QueryView> GetQueryView(Filter filter)
{
using (var context = CreateObjectContext())
{
return (from o in context.QueryViews
where (!filter.FromDate.HasValue || o.RepairDate >= EntityFunctions.TruncateTime(filter.FromDate))
&& (!filter.ToDate.HasValue || o.RepairDate <= EntityFunctions.TruncateTime(filter.ToDate))
select o)
.WithCode(filter)
.InGroup(filter)
.ToList();
}
}
IQueryable Extension:
public static IQueryable<T> WithCode<T>(this IQueryable<T> query, Filter filter)
{
List<string> codes = DAL.GetCodesByCategory(filter.CodeCategories);
if (codes.Count > 0)
return query.Where(Predicates.FilterByCode<T>(codes));
return query;
}
Predicate:
public static Expression<Func<T, List<string>, bool>> FilterByCode<T>(List<string> codes)
{
// Method info for List<string>.Contains(code).
var methodInfo = typeof(List<string>).GetMethod("Contains", new Type[] { typeof(string) });
// List of codes to call .Contains() against.
var instance = Expression.Variable(typeof(List<string>), "codes");
var param = Expression.Parameter(typeof(T), "j");
var left = Expression.Property(param, "Code");
var expr = Expression.Call(instance, methodInfo, Expression.Property(param, "Code"));
// j => codes.Contains(j.Code)
return Expression.Lambda<Func<T, List<string>, bool>>(expr, new ParameterExpression[] { param, instance });
}
The problem I'm having is that Queryable.Where doesn't accept a type of Expression<Func<T, List<string>, bool>. The only way I can think of creating this predicate dynamically is to use two parameters, which is the part that is really stumping me.
What I'm not comprehending is the following method works. I can pass the exact lambda expression I am trying to create dynamically, and it correctly filters my data.
public List<Entities.QueryView> GetQueryView(Filter filter)
{
// Get the codes here.
List<string> codes = DAL.GetCodesByCategory(filter.CodeCategories);
using (var context = CreateObjectContext())
{
return (from o in context.QueryViews
where (!filter.FromDate.HasValue || o.RepairDate >= EntityFunctions.TruncateTime(filter.FromDate))
&& (!filter.ToDate.HasValue || o.RepairDate <= EntityFunctions.TruncateTime(filter.ToDate))
select o)
.Where(p => codes.Contains(p.Code)) // This works fine.
//.WithCode(filter)
.InGroup(filter)
.ToList();
}
}
Questions
Can I implement my own Queryable.Where overload? If so, is it even feasible?
If an overload isn't feasible, is there a way to dynamically construct the predicate p => codes.Contains(p.Code) without using two parameters?
Is there an easier way to do this? I feel like I'm missing something.
You can create your own extension method, name it Where, accept an IQueryable<T>, return an IQueryable<T>, and otherwise make it emulate the form of LINQ methods. It wouldn't be a LINQ method, but it would look like one. I would discourage you from writing such a method simply because it would likely confuse others; even if you want to make a new extension method, use a name not used in LINQ to avoid confusion. In short, do what you're doing now, create new extensions without actually naming them Where. If you really wanted to name one Where though nothing's stopping you.
Sure, just use a lambda:
public static Expression<Func<T, bool>> FilterByCode<T>(List<string> codes)
where T : ICoded //some interface with a `Code` field
{
return p => codes.Contains(p.Code);
}
If you really cannot have your entities implement an interface (hint: you almost certainly can), then the code would look identical to the code that you have, but using the list that you pass in as a constant rather than a new parameter:
public static Expression<Func<T, bool>> FilterByCode<T>(List<string> codes)
{
var methodInfo = typeof(List<string>).GetMethod("Contains",
new Type[] { typeof(string) });
var list = Expression.Constant(codes);
var param = Expression.Parameter(typeof(T), "j");
var value = Expression.Property(param, "Code");
var body = Expression.Call(list, methodInfo, value);
// j => codes.Contains(j.Code)
return Expression.Lambda<Func<T, bool>>(body, param);
}
I would strongly encourage use of the former method; this method loses static type safety, and is more complex and as such harder to maintain.
Another note, the comment you have in your code: // j => codes.Contains(j.Code) isn't accurate. What that lambda actually looks like is: (j, codes) => codes.Contains(j.Code); which is actually noticeably different.
See the first half of #2.