Creating lambda expression ConstantExpression with a string value - c#

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.

Related

Dynamic query - how to do it

I am using the way to build dynamic queries (create a generic mechanism for MongoDB) from: https://michaelscodingspot.com/dynamic-queries/, but somehow it is not working. Below is the code:
public static IQueryable<T> Filter<T>(IQueryable<T> query, string propertyToFilter, string value)
{
ParameterExpression parameterExpression = Expression.Parameter(typeof(T));
MemberExpression memberAccess = Expression.PropertyOrField(parameterExpression, propertyToFilter);
ConstantExpression exprRight = Expression.Constant(value);
BinaryExpression equalExpr = Expression.Equal(memberAccess, exprRight);
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(equalExpr, parameterExpression);
return query.Where(lambda);
}
The error I received is: System.ArgumentNullException: 'Value cannot be null. (Parameter 'itemName')'
When I am using the normal way: query.where(p => p.GroupId == value) everything is working
Hmm, I figure out it:
It is missing the propertyToFilter in the ParameterException
public static IQueryable<T> Filter<T>(IMongoQueryable<T> query, string propertyToFilter, string value)
{
ParameterExpression parameterExpression = Expression.Parameter(typeof(T), propertyToFilter);
MemberExpression memberAccess = Expression.PropertyOrField(parameterExpression, propertyToFilter);
ConstantExpression exprRight = Expression.Constant(value);
BinaryExpression equalExpr = Expression.Equal(memberAccess, exprRight);
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(equalExpr, parameterExpression);
return query.Where(lambda);
}

How to order result dynamically

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

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

Casting the Expression.Property result

How do I create a cast when creating an Expression tree dynamically?
The problem is, I have a property of type string:
public class Test
{
public string Id { get; set; }
}
And I want to generically create a strongly typed lambda expression representing a delegate which returns an object instead of a string (Expression<Func<T, object>>).
Right now I am doing this:
private static Expression<Func<T, object>> CreateIdQuery()
{
Type type = typeof(T);
PropertyInfo idProperty = type.GetProperty("Id");
ParameterExpression lambdaParam = Expression.Parameter(type, "x");
MemberExpression body = Expression.Property(lambdaParam, idProperty);
LambdaExpression expr = Expression.Lambda(body, lambdaParam);
return (Expression<Func<T, object>>)expr;
}
But it throws an exception in the last line (I cannot cast Expression<Func<Test, string>> to Expression<Func<Test, object>>).
How do i cast the body of the expression (I am presuming the MemberExpression part needs to be cast into an object)?
Use Expression.Convert(body, typeof(object)).
private static Expression<Func<T, object>> CreateIdQuery()
{
Type type = typeof(T);
PropertyInfo idProperty = type.GetProperty("Id");
ParameterExpression lambdaParam = Expression.Parameter(type, "x");
MemberExpression body = Expression.Property(lambdaParam, idProperty);
UnaryExpression converted = Expression.Convert(body, typeof(object));
LambdaExpression expr = Expression.Lambda(converted, lambdaParam);
return (Expression<Func<T, object>>)expr;
}

Categories