Dynamic Create Func<T,TR> C# - c#

I'm trying to create a dynamic lambda exp to filter some results on my list, but I can't figure out on how to create a dynamic func<,>
//Here I get the type of my object "Produto", but I can have other objects here...Like "Pedido" or another one.
Type type = typeof(Produto);
var param = Expression.Parameter(type, "arg");
var propName = Expression.Property(param, "Codigo");
var constP = Expression.Constant("123");
var nameStartsWith = Expression.Call(
propName,
typeof(string).GetMethod("StartsWith", new[] { typeof(string) }),
constP);
//Here I have to instantiate my Func<T, bool>
//I can't instantiate it with my variable "type" to use on my Where clausule.
//I don't know how to do this in another way, to create my lambda.
var WhereExp = Expression.Lambda<Func<type, bool>>(nameStartsWith, param);
return _uow.produto.GetAll().AsQueryable().Where(WhereExp).ToList();

You need to create a generic method. Something like this (untested):
public Expression<Func<T, bool>> DynamicWhere<T>(string pname, string value) where T : class
{
var param = Expression.Parameter(typeof(T), "arg");
var propName = Expression.Property(param, pname);
var constP = Expression.Constant(value);
var nameStartsWith = Expression.Call(
propName,
typeof(string).GetMethod("StartsWith", new[] { typeof(string) }),
constP);
return Expression.Lambda<Func<T, bool>>(nameStartsWith, param);
}
Then you can use it like this:
var WhereExp = DynamicWhere<Produto>("Codigo", "123");
return _uow.produto.GetAll().AsQueryable().Where(WhereExp).ToList();

Related

Dynamically create generic type of an Expression.Lambda

I would like to create lambda expressions, type:
Expression.Lambda <Func<T,TProperty>>
  but without knowing the TProperty, to save them in a collection.
public BuilderMapping<T> AutoMap()
{
var type = typeof(T);
var props = type.GetProperties(BindingFlags.Public);
foreach (var propertyInfo in props)
{
var param = Expression.Parameter(typeof(T));
var body = Expression.PropertyOrField(param, propertyInfo.Name);
// propertyInfo.PropertyType == TProperty
// I have not TProperty
var exp = Expression.Lambda<Func<T, TProperty>>(body, param);
var prop = new PropertyDescriptor<T, TProperty>
{
Selector = exp
};
_descriptorColumn.Add(prop);
}
return this;
}
var delegateType = typeof(Func<,>).MakeGenericType(typeof(T), propertyInfo.PropertyType);
var exp = Expression.Lambda(delegateType, body, param);
PropertyDescriptor<T, TProperty> will need similar treatment, I think the shortest way would be this:
dynamic prop = Activator.CreateInstance(typeof(PropertyDescriptor<,>).MakeGenericType(typeof(T), propertyInfo.PropertyType));
prop.Selector = (dynamic)exp;

OrderBy with a dynamic string parameter in Lambda with related table

I have a extention orderByField
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 use it for pass string parameter in it
string sortBy = "StatusID";
return dbContext.Requests.OrderByField(sortBy,true)
But how i can orderBy with a dynamic string parameter with column related with request table like
dbContext.Requests.OrderBy(x => x.Status.Description)
Assuming that you would receive SortField argument in the form of "Status.Description", you would:
First have to split argument to get all properties
Iterate over property names and building property accessor expressions
Use built expression as body of lambda expression
For example:
public static IQueryable<T> OrderByField<T>(this IQueryable<T> q, string SortField, bool Ascending)
{
var param = Expression.Parameter(typeof(T), "p");
Expression expBody = param;
string[] props = SortField.Split('.');
foreach (var prop in props)
{
expBody = Expression.Property(expBody, prop);
}
var exp = Expression.Lambda(expBody, 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);
}

Creating linq expression trees for dynamic objects

I am trying to build an expression tree dynamically for sorting. The sorting will happen at the action filter of my web api. So the type of object will be unknown until runtime.
Here's the overview:
At the action filter level:
IEnumerable<object> model = null;
context.Response.TryGetContentValue(out model);
model=model.OrderByExtension(orderByField, orderDirection);
context.Response.Content=new ObjectContent<IEnumerable<object>>(model, new JsonMediaTypeFormatter());
And the extension method:
public static IQueryable<T> OrderByExtension<T>(this IQueryable<T> source, string sortProperty, Sorting.SortingOption sortOrder)
{
var type = source.FirstOrDefault().GetType(); //Gets the type of object passed, since typeof(T) is only object at this point
var property = type.GetProperty(sortProperty);
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExp = Expression.Lambda(propertyAccess, parameter);
var typeArguments = new Type[] { typeof(T), property.PropertyType };
var methodName = sortOrder == Sorting.SortingOption.Asc ? "OrderBy" : "OrderByDescending";
var resultExp = Expression.Call(typeof(Queryable), methodName, typeArguments, source.Expression, Expression.Quote(orderByExp));
return source.Provider.CreateQuery<T>(resultExp);
}
On the Expression.Call - I get the error: No generic method 'OrderByDescending' on type 'System.Linq.Queryable' is compatible with the supplied type arguments and arguments.
I am assuming there is a mismatch between the type 'object' and the actual type when the OrderBy method is invoked.
Is there anyway to get this to work ?
Thanks in advance.
ps: I also tried to create a generic method for OrderBy via .MakeGenericMethod - but no success.
The problem is that you're generating an expression of a function of the actual underlying type, not the type of the source expression (object). You'll need to add a conversion to your type in the generated expression.
public static IQueryable<object> OrderByExtension(this IQueryable<object> source, string sortProperty, SortOrder sortOrder = SortOrder.Unspecified)
{
var sourceType = typeof(object);
var underlyingType = source.First().GetType();
var propertyType = underlyingType.GetProperty(sortProperty).PropertyType;
var param = Expression.Parameter(sourceType);
var body = Expression.Property(
Expression.Convert(param, underlyingType), sortProperty
);
var lambda = Expression.Lambda(body, param);
var sortMethod = sortOrder == SortOrder.Descending ? "OrderByDescending" : "OrderBy";
var expr = Expression.Call(typeof(Queryable), sortMethod, new Type[] { sourceType, propertyType },
source.Expression, lambda
);
return source.Provider.CreateQuery<object>(expr);
}

Construct expression tree with chained properties?

I have a method that accepts Expression<Func<T, string>>, for example x => x.Name, and a term, and returns x => x.Name.Contains(term):
Given the model;
class X
{
public Y Y {get; set;}
}
class Y
{
public string Z {get; set;}
}
It works well for GenerateForMember<Y>(y => y.Z, "foobar"), but currently don't work for GenerateForMember<X>(x => x.Y.Z, "foobar"). It gives the exception
'Z' is not a member of 'UserQuery+X'
How to I update my method to work with chained properties?
Method is as follows:
protected Expression<Func<T, bool>> GenerateForMember<T>(Expression<Func<T,string>> expression, string term)
{
var type = typeof(T);
var memberExpression = ((expression.Body.NodeType == ExpressionType.Convert)
? ((UnaryExpression)expression.Body).Operand
: expression.Body) as MemberExpression;
ParameterExpression parameter = Expression.Parameter(type, type.Name.ToLower());
MemberExpression member = Expression.PropertyOrField(parameter, memberExpression.Member.Name);
var propertyInfo = memberExpression.Member as PropertyInfo;
var memberType = propertyInfo == null
? ((FieldInfo) memberExpression.Member).FieldType
: propertyInfo.PropertyType;
ConstantExpression constant = Expression.Constant(term, typeof(string));
MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var containsMethodExp = Expression.Call(member, method, constant);
return Expression.Lambda<Func<T, bool>>(containsMethodExp, parameter);
}
You are dissecting the original expression and later re-construct it again. This is not necessary. You can use expression.Body directly in order to create the method call. Like this, it should work with any lambda expression.
var type = typeof(T);
ParameterExpression parameter = Expression.Parameter(type, type.Name.ToLower());
ConstantExpression constant = Expression.Constant(term, typeof(string));
MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var containsMethodExp = Expression.Call(expression.Body, method, constant);
return Expression.Lambda<Func<T, bool>>(containsMethodExp, parameter);
Try this:
protected Expression<Func<T, bool>> GenerateForMember<T>(Expression<Func<T, string>> expression, string term)
{
ConstantExpression constant = Expression.Constant(term, typeof(string));
MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var containsMethodExp = Expression.Call(expression.Body, method, constant);
return Expression.Lambda<Func<T, bool>>(containsMethodExp, expression.Parameters[0]);
}

Dynamic Expression from the Property of the object of the class

i'm starting to explore dynamic expressions, so please help me to solve one issue.
I have an object
public class Categorisation{
string Name{get;set;}
}
public class Client{
public Categorisation Categorisation{get;set;}
}
All I need is to write a dynamic expression and call Categorisation.Name.Equals("A1") from Client object.
x=>x.Categorisation.Name.Equals("A1")
How can I do this using Expressions?
var param = Expression.Parameter(typeof(Client));
var prop = Expression.Property(param, typeof(Client).GetProperty("Categorisation"));
var argument = Expression.Constant("A1");
var method = typeof(string).GetMethod("Equals", new[] { typeof(string) });
var call = Expression.Call(prop, method);
var expr = Expression.Lambda<Func<Client, bool>>(call, param);
Of course this code is wrong and I call method Equals from Categorisation property and not from Name of Categorisation. But how to invoke Name property?
var param = Expression.Parameter(typeof(Client));
var prop = Expression.Property(param, typeof(Client).GetProperty("Categorisation"));
var namePropExpr = Expression.Property(prop, "Name");
var argument = Expression.Constant("A1");
var method = typeof(string).GetMethod("Equals", new[] { typeof(string) });
var call = Expression.Call(namePropExpr, method, argument);
var expr = Expression.Lambda<Func<Client, bool>>(call, param);

Categories

Resources