I need to extend the Where method for IQueryable to something like this:
.WhereEx("SomeProperty", "==", "value")
I dont know if this is even possible, but i found this in SO : Unable to sort with property name in LINQ OrderBy
I tried this answer and it seems quite interesting (Ziad's answer):
using System.Linq;
using System.Linq.Expressions;
using System;
namespace SomeNameSpace
{
public static class SomeExtensionClass
{
public static IQueryable<T> OrderByField<T>(this IQueryable<T> q, string SortField, bool Ascending)
{
var param = Expression.Parameter(typeof(T), "p");
var prop = Expression.Property(param, SortField);
var exp = Expression.Lambda(prop, param);
string method = Ascending ? "OrderBy" : "OrderByDescending";
Type[] types = new Type[] { q.ElementType, exp.Body.Type };
var mce = Expression.Call(typeof(Queryable), method, types, q.Expression, exp);
return q.Provider.CreateQuery<T>(mce);
}
}
}
Is It possible to do the same with the Where method ?
Thanks.
Update :
I can now set an expression to pass to the Call method, but i get the following exception: "No generic method 'Where' on type 'System.Linq.Queryable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic."
here is my code:
public static IQueryable<T> WhereEx<T>(this IQueryable<T> q, string Field, string Operator, string Value)
{
var param = Expression.Parameter(typeof(T), "p");
var prop = Expression.Property(param, Field);
var val = Expression.Constant(Value);
var body = Expression.Equal(prop, val);
var exp = Expression.Lambda<Func<T, bool>>(body, param);
Type[] types = new Type[] { q.ElementType, typeof(bool) };
var mce = Expression.Call(
typeof(Queryable),
"Where",
types,
exp
);
return q.Provider.CreateQuery<T>(mce);
}
Notice that i dont use the Operator argument yet, instead i use Equal for debugging purpose.
Can someone help me with this please ?
I did another post with a simplified question. the link is Here
public static IQueryable<T> WhereEx<T>(this IQueryable<T> q, string Field, string Operator, string Value)
{
var param = Expression.Parameter(typeof(T), "p");
var prop = Expression.Property(param, Field);
var val = Expression.Constant(Value);
var body = Expression.Equal(prop, val);
var exp = Expression.Lambda<Func<T, bool>>(body, param);
return System.Linq.Queryable.Where(q, exp);
}
Related
I am trying to apply sorting dynamically. I am using EntityFramework. I am passing sortorder and sortfield. Now I don't want to write conditions to order result by sortfield column. What I have tried is as below,
public static IOrderedQueryable<TSource> OrderByProperty<TSource, TKey>(this IQueryable<TSource> source, string property, string sortorder)
{
ParameterExpression param = Expression.Parameter(typeof(TSource), "t");
MemberExpression member = Expression.Property(param, property);
var ex = Expression.Lambda<Func<TSource, TKey>>(member, param);
return source.OrderBy<TSource, TKey>(ex);
}
And I call it as below,
OrderByProperty<Class, dynamic>(objClass, sortfield, sortorder);
I am getting below error when sortfield is of type System.Int32;
Expression of type 'System.Int32' cannot be used for return type 'System.Object'
same for string. Any help is appreciated.
Try this extension method:
public static IQueryable<T> OrderByField<T>(this IQueryable<T> q, string SortField, bool Ascending)
{
var param = Expression.Parameter(typeof(T), "p");
var prop = Expression.Property(param, SortField);
var exp = Expression.Lambda(prop, param);
string method = Ascending ? "OrderBy" : "OrderByDescending";
Type[] types = new Type[] { q.ElementType, exp.Body.Type };
var mce = Expression.Call(typeof(Queryable), method, types, q.Expression, exp);
return q.Provider.CreateQuery<T>(mce);
}
I have the following class and extension method to invoke String.Contains method. How can I change it to be case insensitive ? Something like in Expression tree for String.IndexOf method but I don't have an ideas so far how to adjust that code into my code. Any help ?
public class testItem
{
public string SomeProperty { get; set; }
}
public static IQueryable<testItem> PropertyContainsNEW<testItem>(this IQueryable<testItem> source,
Expression<Func<testItem, string>> selector,
string value)
{
ParameterExpression parameter = Expression.Parameter(typeof(testItem), "x");
Expression property = Expression.Property(parameter, ((MemberExpression)selector.Body).Member.Name);
var search = Expression.Constant(value, typeof(string));
MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var containsMethodExp = Expression.Call(property, method, search);
var predicate = Expression.Lambda<Func<testItem, bool>>(containsMethodExp, parameter);
return source.Where(predicate);
}
In order to use the StringComparison parameter, you need to correctly identify that method.
Is this what you need?:
public static IQueryable<testItem> PropertyContainsNEW(this IQueryable<testItem> source,
Expression<Func<testItem, string>> selector,
string value)
{
var parameter = Expression.Parameter(typeof(testItem), "x");
var property = Expression.Property(parameter, ((MemberExpression)selector.Body).Member.Name);
var search = Expression.Constant(value, typeof(string));
var parms = new Expression[] { search,
Expression.Constant(StringComparison.OrdinalIgnoreCase) };
var method = typeof(string).GetMethod("Contains", new[] { typeof(string), typeof(StringComparison) });
var containsMethodExp = Expression.Call(property, method, parms);
var predicate = Expression.Lambda<Func<testItem, bool>>(containsMethodExp, parameter);
return source.Where(predicate);
}
I have a problem converting simple linq query to Lambda Expression.
My queries look like this:
int[] array = List<int> array2 = sql.OfType<Table1>().Select(x=>x.ID).Take(10).ToList();
var result = sql.OfType<Table1>().Where(x => array.Contains(x.ID)).Take(10).ToList();
and the final result should be:
static void DynamicSQLQuery<T>(IQueryable<T> sql, string fieldName)
{
List<int> array = sql.OfType<T>().Select(SelectExpression<T>(fieldName)).Take(10).ToList();
var result = sql.OfType<T>().Where(InExpression<T>(fieldName, array)).Take(10).ToList();
}
Class
public class Table1
{
public int ID { get; set; }
public string Name { get; set; }
}
I already converted the first lambda:
public static Expression<Func<T, int>> SelectExpression<T>(string fieldName)
{
ParameterExpression param = Expression.Parameter(typeof(T), "x");
MemberExpression selection = Expression.PropertyOrField(param, fieldName);
var lambdaExp = Expression.Lambda<Func<T, int>>(selection, param);
return lambdaExp;
}
But stuck on the second one:
static Expression<Func<T, bool>> InExpression<T>(string propertyName,IEnumerable<int> array)
{
System.Reflection.MethodInfo containsMethod = typeof(IEnumerable<int>).GetMethod("Contains");
ParameterExpression param = Expression.Parameter(typeof(T), "x");
MemberExpression member = Expression.PropertyOrField(param, propertyName);//x.{property}
var constant = Expression.Constant(3);
var body = Expression.GreaterThanOrEqual(member, constant); //x.{property} >= 3 but I need array.Contains(x.{property})
var finalExpression = Expression.Lambda<Func<T, bool>>(body, param);
return finalExpression;
}
Can anyone help me to make the lambda expression x=>array2.Contains(x.ID) in InExpression method?
Also, I'll be very grateful for some link to article/tutorial about creating these type of expressions.
Probably something like:
static Expression<Func<T, bool>> InExpression<T>(
string propertyName, IEnumerable<int> array)
{
var p = Expression.Parameter(typeof(T), "x");
var contains = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
.Single(x => x.Name == "Contains" && x.GetParameters().Length == 2)
.MakeGenericMethod(typeof(int));
var property = Expression.PropertyOrField(p, propertyName);
var body = Expression.Call(contains, Expression.Constant(array), property);
return Expression.Lambda<Func<T, bool>>(body, p);
}
The trick here is to start off with something simple that compiles; for example:
using System.Linq;
using System;
using System.Linq.Expressions;
using System.Collections.Generic;
public class C {
static Expression<Func<Foo, bool>> InExpression<T>(
string propertyName,IEnumerable<int> array)
{
return x => array.Contains(x.Id);
}
}
class Foo {
public int Id {get;set;}
}
Now either compile it and look in ildasm/reflector, or (and much simpler): run that through https://sharplab.io specifying C# as the output, like this
This shows you the code that the compiler generated:
private static Expression<Func<Foo, bool>> InExpression<T>(string propertyName, IEnumerable<int> array)
{
C.<>c__DisplayClass0_0<T> <>c__DisplayClass0_ = new C.<>c__DisplayClass0_0<T>();
<>c__DisplayClass0_.array = array;
ParameterExpression parameterExpression = Expression.Parameter(typeof(Foo), "x");
Expression arg_77_0 = null;
MethodInfo arg_77_1 = methodof(IEnumerable<!!0>.Contains(!!0));
Expression[] expr_38 = new Expression[2];
expr_38[0] = Expression.Field(Expression.Constant(<>c__DisplayClass0_, typeof(C.<>c__DisplayClass0_0<T>)), fieldof(C.<>c__DisplayClass0_0<T>.array));
Expression[] expr_5F = expr_38;
expr_5F[1] = Expression.Property(parameterExpression, methodof(Foo.get_Id()));
Expression arg_86_0 = Expression.Call(arg_77_0, arg_77_1, expr_5F);
ParameterExpression[] expr_82 = new ParameterExpression[1];
expr_82[0] = parameterExpression;
return Expression.Lambda<Func<Foo, bool>>(arg_86_0, expr_82);
}
Note that there's a few things in here we need to fixup, but it allows us to see what it is doing - things like memberof and fieldof don't actually exist, for example, so we need to look them up via reflection.
A humanized version of the above:
private static Expression<Func<Foo, bool>> InExpression<T>(string propertyName, IEnumerable<int> array)
{
ExpressionState state = new ExpressionState();
state.array = array;
ParameterExpression parameterExpression = Expression.Parameter(typeof(Foo), "x");
MethodInfo contains = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
.Single(x => x.Name == nameof(Enumerable.Contains) && x.GetParameters().Length == 2)
.MakeGenericMethod(typeof(int));
Expression[] callArgs = new Expression[2];
callArgs[0] = Expression.Field(Expression.Constant(state, typeof(ExpressionState)), nameof(ExpressionState.array));
callArgs[1] = Expression.Property(parameterExpression, propertyName);
Expression body = Expression.Call(null, contains, callArgs);
ParameterExpression[] parameters = new ParameterExpression[1];
parameters[0] = parameterExpression;
return Expression.Lambda<Func<Foo, bool>>(body, parameters);
}
with:
class ExpressionState
{
public IEnumerable<int> array;
}
Hi I am trying to use that code to get property User.Email of customer that contains User. but its an object (User of type User) so it throws exception. what should i fix?
public static IQueryable<T> OrderByField<T>(this IQueryable<T> q, string SortField, bool Ascending)
{
if (!string.IsNullOrWhiteSpace(SortField))
{
var param = Expression.Parameter(typeof (T), "p");
var prop = Expression.Property(param, SortField);
var exp = Expression.Lambda(prop, param);
string method = Ascending ? "OrderBy" : "OrderByDescending";
Type[] types = new Type[] {q.ElementType, exp.Body.Type};
var mce = Expression.Call(typeof (Queryable), method, types, q.Expression, exp);
return q.Provider.CreateQuery<T>(mce);
}
else
{
return q;
}
}
You need to pass a property path to the function (like "User.Email") and account for that inside, like this
public static IQueryable<T> OrderByField<T>(this IQueryable<T> source, string sortField, bool ascending)
{
if (string.IsNullOrWhiteSpace(sortField)) return source;
var item = Expression.Parameter(typeof(T), "item");
Expression member = null;
foreach (var memberName in sortField.Split('.'))
member = Expression.PropertyOrField(member ?? item, memberName);
var selector = Expression.Lambda(member, item);
var method = ascending ? "OrderBy" : "OrderByDescending";
var types = new [] { source.ElementType, selector.Body.Type };
var expression = Expression.Call(typeof(Queryable), method, types, source.Expression, selector);
return source.Provider.CreateQuery<T>(expression);
}
The essential part is
var item = Expression.Parameter(typeof(T), "item");
Expression member = null;
foreach (var memberName in sortField.Split('.'))
member = Expression.PropertyOrField(member ?? item, memberName);
which starts from the expression parameter and builds an accessor for each member specified in the path.
I have function do dynamic linq where to dbset on dbcontext but get error No method 'Where' exists on type System.Linq.IQueryable
I don't now!!!
using System.Linq;
public virtual Queryable _List(string fieldNames="", string values="")
{
_Db = Contex.Set<T>();
var type = typeof(T);
var property = type.GetProperty(fieldNames);
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExp = Expression.Lambda(propertyAccess, parameter);
var body2 = Expression.Call(
typeof(Queryable),
"Where",
// I things this line no good but I don't now ...
new Type[] { type, property.PropertyType },
_Db.Expression,
Expression.Quote(orderByExp));
var m = _Db.Provider.CreateQuery<T>(body2);
return m;
}
There's no overload of Queryable.Where that has two type parameters. You're probably using this method:
IQueryable<TSource> Where<TSource> (this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
Which means you should replace the line:
new Type[] { type, property.PropertyType },
With:
new Type[] { type }, // this is TSource