How to order result dynamically - c#

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);
}

Related

get property of inner object

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.

How to get Max value from Dynamic Linq

How to get Max value from Dynamic Linq ?
public Func<TSource, T> DynamicMax<TSource, T>(string fieldName, object className)
{
var type = typeof(TSource);
var pe = Expression.Parameter(type, "p");
var propertyReference = Expression.Property(pe, fieldName);
var constantReference = Expression.Constant(className);
return Expression.Lambda<Func<TSource, T>>
(Expression.IsTrue(propertyReference, constantReference), pe).Compile();
}

extend Where for IQueryable

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);
}

No method 'Where' exists on type 'System.Linq.IQueryable'

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

Creating lambda expression ConstantExpression with a string value

I want to create lambda expression providing property name, a value (as string) and property type (as Type).
The problem with that is in line Expression.Constant(value1, propertyType);
value1 that is passed to Foo is string. and must be parsed to "unknown" type
static Expression<Func<T, bool>> LabmdaExpression<T>(string property1, string value1,
Type propertyType)
{
var parameterExpression = Expression.Parameter(typeof(TheObject), "o");
var memberExpression1 = Expression.PropertyOrField(parameterExpression, property1);
//casting?
var valueExpression1 = Expression.Constant(value1, propertyType);
var binaryExpression1 = Expression.GreaterThan(memberExpression1, valueExpression1);
return Expression.Lambda<Func<T, bool>>(binaryExpression1, parameterExpression);
}
I think you should have T where you currently have TheObject.
To convert the string, you can call the Convert.ChangeType() method in your expression and cast the resulting object:
static readonly MethodInfo ChangeTypeMethod = typeof(Convert).GetMethod(
"ChangeType", new[] { typeof(object), typeof(Type) });
static Expression<Func<T, bool>> LabmdaExpression<T>(
string property1, string value1, Type propertyType)
{
ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "o");
MemberExpression memberExpression1 = Expression.PropertyOrField(
parameterExpression, property1);
Expression convertedObject = Expression.Call(
ChangeTypeMethod, Expression.Constant(value1),
Expression.Constant(propertyType));
Expression converted = Expression.Convert(convertedObject, propertyType);
BinaryExpression binaryExpression1 = Expression.GreaterThan(
memberExpression1, converted);
return Expression.Lambda<Func<T, bool>>(binaryExpression1, parameterExpression);
}
Just casting won't work, because code like (int)"42" is not valid.

Categories