Turn a property name as a string into a Linq Expression - c#

I need to turn an string that represents a property name on an interface into an expression. I have most of it working, there is just 1 piece that I can't figure out at the end.
static Expression<Func<T, int>> MakeGetter<T>(string propertyName)
{
var input = Expression.Parameter(typeof(T));
var property = typeof(T).GetProperty(propertyName) ??
GetProperty(propertyName, typeof(T));
var expr = Expression.Property(input, property);
var propType = property.PropertyType.Name;
switch (propType.ToLower())
{
case "string":
return Expression.Lambda<Func<T, string>>(expr, input);
case "int":
return Expression.Lambda<Func<T, int>>(expr, input);
}
}
private static PropertyInfo GetProperty(string propertyName, Type i)
{
var baseInterfaces = i.GetInterfaces();
foreach (var baseInterface in baseInterfaces)
{
var property = baseInterface.GetProperty(propertyName);
return property ?? GetProperty(propertyName, baseInterface);
}
return null;
}
The one problem that I have is at the end of the MakeGetter function I don't know if the function is a string or int or some other type and have no way of knowing until after I do all of the reflection, so how can I create this method, so that it is generic and will return an Expression correctly.

Have a look here at an example of generating lambda expressions at runtime. These lines from the article show how to generate lambda expression:
var parameterExpression = Expression.Parameter(typeof(TEntity), "x");
var memberExpression = Expression.PropertyOrField(parameterExpression, prop.Name);
var memberExpressionConversion = Expression.Convert(memberExpression, typeof(object));
var lambda = Expression.Lambda<Func<TEntity, object>>(memberExpressionConversion, parameterExpression);

Now that you've declared your intent for this in the comments, I believe that the accepted answer here is what you're looking for. Even if it's not doing exactly what you're looking for, these lines can carry over:
// TODO: Get 'type' from the property you want.
Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
You could return LambdaExpression, or just use the extension methods found in that answer, or use DynamicLinq. I guess the moral of the story (as it often is) is that its probably been done already :).

Related

How do I use reflection to get a property and use it in a query?

I have a generic method and I want to add a search capability to my method. as parameter I get the name of property(string) and the value(string) it should search for in the list. how can I achieve this?
**This code is not the exact code I have so it may seem that I can use other options like Expression functions which is not possible in my case cause it should be consumed in an Api Controller
**I use unit of work with repository pattern in real project and for sake of simplicity I have tryed to add it up in one simple function
public async Task<ActionResult<List<T>>> GetAll(string? filterProperty = null, string? filterValue = null)
{
IQueryable<T> query = dbSet;
if (filterProperty != null)
{
PropertyInfo property = typeof(T).GetProperty(filterProperty);
query = query. Where(u=> u.property.Contains(filterValue));
}
return await query.ToListAsync();
}
For IQueryable you'll want to create a LambdaExpression for the filter predicate. (For IEnumerable you can compile that expression into an appropriate Func<>.)
This all works by building an expression tree that represents the action you want to perform. In this case you're calling Contains on the result of getting the property value, passing a constant for the filter value.
Let's start with the Contains method, which you can reuse. Rather than basic reflection, here's how you can get it using an expression:
static readonly MethodInfo _contains =
(((Expression<Func<string, bool>>)(s => s.Contains("a"))).Body as MethodCallExpression)
.Method;
While that might look a little confusing, it's leveraging the compiler to do the reflection work for us. Sometimes it's easier than searching for the right version of a method with multiple overloads, or when it's not obvious which extension method is involved. The result here is that _contains gets initialized with the method info we need.
You've already got the property info for the target property, so let's put them together:
// The parameter for the predicate
var row = Expression.Parameter(typeof(T), "row");
// Constant for the filter value
var filter = Expression.Constant(filterValue);
// Get the value of the property
var prop = Expression.Property(property);
// Call 'Contains' on the property value
var body = Expression.Call(prop, _contains, filter);
// Finally, generate the lambda
var predicate = Expression.Lambda<Func<T, bool>(body, row);
// Apply to the query
query = query.Where(predicate);
Or in slightly more compact form:
var row = Expression.Parameter(typeof(T), "row");
var predicate =
Expression.Lambda<Func<T, bool>
(
Expression.Call
(
Expression.Property(row, property),
_contains,
Expression.Constant(filterValue)
),
row
);
When you're working on data via IEnumerable<T> instead, predicate.Compile() will produce a working Func<T, bool> to pass to IEnumerable.Where():
private static readonly MethodInfo _tostring = typeof(Object).GetMethod("ToString");
static readonly MethodInfo _compare = (((Expression<Func<string, bool>>)(s => s.Contains(""))).Body as MethodCallExpression).Method;
public static IEnumerable<T> Search<T>(this IEnumerable<T> items, string propertyName, string filterValue)
{
var property = typeof(T).GetProperty(propertyName);
var row = Expression.Parameter(typeof(T), "row");
// Let's make sure we're actually dealing with a string here
Expression prop = Expression.Property(row, property);
if (property.PropertyType != typeof(string))
prop = Expression.Call(prop, _tostring);
var func =
Expression.Lambda<Func<T, bool>>
(
Expression.Call
(
prop,
_compare,
Expression.Constant(filterValue)
),
row
).Dump().Compile();
return items.Where(func);
}
Expressions are pretty versatile, and there are a lot of places where they come in handy. It can be more efficient to compose a function and call it multiple times than to go through reflection all the time, and you can do interesting things with merging expressions and so on.

How to initialize the Expression.Call object in System.Linq.Expression C#

I want to initialize the Expression Call outside the if else condition. As i have to use it to generate expression body because i have two different type coming from database i.e int and int?. My Code is below.
I am getting error to instantiate the toString object.
var parameterExp = Expression.Parameter(typeof(T), "type");
var propertyExp = Expression.Property(parameterExp, propertyName);
MethodInfo method = typeof(string).GetMethod(methodType, new[] { typeof(string) });
var searchValue = Expression.Constant(propertyValue.ToLower(), typeof(string));
var toString = new MethodCallExpression();
if (propertyName == nameof(CustomerListDto.Id))
{
toString = Expression.Call(propertyExp, typeof(int).GetMethod("ToString", System.Type.EmptyTypes));
}
else
{
toString = Expression.Call(propertyExp, typeof(int?).GetMethod("ToString", System.Type.EmptyTypes));
}
var body = Expression.Call(toString, method, searchValue);
return Expression.Lambda<Func<T, bool>>(body, parameterExp);
I don't know exactly how to initialize ExpressionCall. This is the thing which i want to know. Currently it is giving me error "MethodCallExpression does not contains constructor that takes 0 argument". I searched a lot but could not find any solution.
You can't instantiate a MethodCallExpression manually as the constructor is private. You can get an instance of MethodCallExpression as the return value of Expression.Call though. You probably just want to declare it as
MethodCallExpression toString;
// Then assign it with Expression.Call(...);
if (propertyName == nameof(CustomerListDto.Id))
{
toString = Expression.Call(propertyExp, typeof(int).GetMethod("ToString", System.Type.EmptyTypes));
}
else
{
toString = Expression.Call(propertyExp, typeof(int?).GetMethod("ToString", System.Type.EmptyTypes));
}

IQueryable GroupBy Lambda expression fails for primitive types with object key

I am in process of creating Lambda Expression for an IQueryable<TSource>, following is my extension method code, which I need to call like:
queryableData.GroupBy<int,Product>("ID")
queryableData.GroupBy<string,Product>("Name")
public static IQueryable<IGrouping<TKey,TSource>> GroupBy<TKey,TSource>(this IQueryable<TSource> queryable, string propertyName)
{
// Access the propertyInfo, using Queryable Element Type (Make it Case insensitive)
var propInfo = queryable.ElementType.GetProperty(propertyName,BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
// Access the Collection / Queryable Type
var collectionType = queryable.ElementType;
// Creating Group Parameter Expression
var groupParameterExpression = Expression.Parameter(collectionType, "g");
// Create MemberEXpression with the Property (access the property of a Type)
var propertyAccess = Expression.MakeMemberAccess(groupParameterExpression, propInfo);
// Create Lambda Expression
var lambdaExpression = Expression.Lambda<Func<TSource,TKey>>(propertyAccess, groupParameterExpression);
// Return GroupBy result
return queryable.GroupBy(lambdaExpression);
}
My aim is to generate Expression<Func<TSource,object>> instead of Expression<Func<TSource,TKey>>, so that it can be called without providing the Key type, following is code:
public static IQueryable<IGrouping<object, TSource>> GroupByObjectKey<TSource>(this IQueryable<TSource> queryable, string propertyName)
{
// Access the propertyInfo, using Queryable Element Type (Make it Case insensitive)
var propInfo = queryable.ElementType.GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
// Access the Collection / Queryable Type
var collectionType = queryable.ElementType;
// Creating Group Parameter Expression
var groupParameterExpression = Expression.Parameter(collectionType, "g");
// Create MemberEXpression with the Property (access the property of a Type)
var propertyAccess = Expression.MakeMemberAccess(groupParameterExpression, propInfo);
// Create Lambda Expression
var lambdaExpression = Expression.Lambda<Func<TSource, object>>(propertyAccess, groupParameterExpression);
// Return GroupBy result
return queryable.GroupBy(lambdaExpression);
}
Now I able to make it work for string type as follows:
queryableData.GroupBy<Product>("Name")
but it fails on following call for integer type with exception as stated underneath:
queryableData.GroupBy<Product>("Id")
Expression of type 'System.Int32' cannot be used for return type 'System.Object'
This is a clear case of Type conversion, but I am surprised that why a type would refuse to be converted to Object base class, what could be the rationale, any pointer / suggestion
As comments already pointed out, you need to add a conversion to object:
var convertToObject = Expression.Convert(propertyAccess, typeof(object));
var lambdaExpression = Expression.Lambda<Func<TSource, object>>(convertToObject, groupParameterExpression);

Call method Expression Tree in C#

I want create expression with reflection in c#.
I am target script is:
using (var service = new MyProxy<IProductsService>())
{
var response = service.DoAction<Response<List<Product>>>(srv => srv.GetProducts());
}
How to (srv => srv.GetProducts() script is generate with expression tree?
Edit:
I am sorry for the wrong expression.
wherein IProductsService, Response<List<Product>> and GetProducts are actually unknown types. I take generic on runtime.
I wrote the following methods(CreateExpression) to try. But Expression property = Expression.Call(methodReturnType, methodName.Name, new Type[]{ } ,parameter); line gives the following error:
No method 'GetProducts' exists on type
'ServiceFoundation.Response1[System.Collections.Generic.List1[ServiceFoundation.Products.Product]]'.
Next in line for I have not tested it yet.
Method:
private void CreateExpression(Type interfaceType, Type methodReturnType, MethodInfo methodName)
{
ParameterExpression parameter = Expression.Parameter(interfaceType, "srv");
Expression property = Expression.Call(methodReturnType, methodName.Name, new Type[]{ } ,parameter);
Type expressionType = typeof(Expression<>);
Type lambdaType = typeof(LambdaExpression);
Type funcType = typeof(Func<,>);
Type delegateType = funcType.MakeGenericType(interfaceType, methodReturnType);
Type expression = expressionType.MakeGenericType(delegateType);
MethodInfo mI = typeof(Expression).GetMethod("Lambda");
MethodInfo lambda = mI.MakeGenericMethod(delegateType);
var ex = lambda.Invoke(this, new object[] { delegateType, property, parameter });
}
Hopefully I can express.
If you have an
Expression<Action<Response>>
you can call
.Compile()
on it and it returns an
Action<Response>
which you can then invoke normally.
eg
Expression<Action<Response>> exp = resp => Console.WriteLine(resp);
Action<Response> func = exp.Compile();
func(myResponse);
However, if that's all you need to do, you might find it simpler not to use expressions at all;
Action<Response> func = resp => Console.WriteLine(resp);
func(myResponse);
I succeeded after a long struggle. I examined different questions here.
and in the end I solved this way:
private Expression CreateExpression(Type interfaceType, Type methodReturnType, MethodInfo methodName)
{
ParameterExpression parameter = Expression.Parameter(interfaceType, "srv");
Expression callExpression = Expression.Call(parameter, methodName.Name,null, null);
Type expressionType = typeof(Expression<>);
Type lambdaType = typeof(LambdaExpression);
Type funcType = typeof(Func<,>).MakeGenericType(interfaceType, methodReturnType);
Type expressionGenericType = expressionType.MakeGenericType(funcType);
string methodSignature = "System.Linq.Expressions.LambdaExpression Lambda(System.Linq.Expressions.Expression, System.Linq.Expressions.ParameterExpression[])";
var lambdaMethod = typeof(Expression).GetMethods()
.Single(mi => mi.ToString() == methodSignature);
Expression lambdaExpression = (Expression)lambdaMethod.Invoke(this, new object[] { callExpression, new ParameterExpression[] { parameter } });
return lambdaExpression;
}

How to create a generic lambda from two expression trees?

First of all I want to make you know that I've searched another questions and answers before making this one, but I cound't find any that would help me in this specific problem I am facing.
I need to filter registers based in two properties of its class, one of them being the field corresponding to the search, and the other one being a numeric code of another entity to which the register must be referenced in the database.
My search function has the following signature:
public List<TView> SearchByField(int parentCode, string fieldName, string filter);
I've tried to implement this using Expression Trees, and got it to get two expressions, but now I didn't get to combine these expressions to build one to pass to the final
Expression.AndAlso(parentCodeFilterExpression, textFilterExpression);
that will combine the too expressions in only one.
What I got so far was the code shown below (sorry for the long snippet, but I think this was necessary to make it easier to understand the question):
public List<TView> SearchPerField(int parentCode, string fieldName, string filter)
{
var lambdaExpression = GetLambdaExpressionForSearchByField(fieldName, filter, parentCode);
return new PersistenciaImpl<TView>().Where(lambdaExpression).ToList();
}
private Expression<Func<TView, bool>> GetLambdaExpressionForSearchByField(string fieldName, string filter, int parentCode)
{
Expression<Func<TView, bool>> textFilterExpression = GetTextFilterExpression(fieldName, filter);
Expression<Func<TView, bool>> parentCodeFilterExpression = GetParentCodeFilterExpression(parentCode);
Expression.Lambda<Func<TView, bool>>(textFilterExpression, parentCodeFilterExpression);
// THIS IS THE POINT. HOW TO MAKE THIS WORK?
Expression.AndAlso(parentCodeFilterExpression, textFilterExpression);
return textFilterExpression;
}
private Expression<Func<TView, bool>> GetParentCodeFilterExpression(int parentCode)
{
ParameterExpression parameter = Expression.Parameter(typeof(TView), "x");
Expression parent = Expression.Property(parameter, "Parent");
Expression parentCodeExpression = Expression.Property(parent, "Code");
Expression target = Expression.Constant(parentCode);
Expression containsMethod = Expression.Call(parentCodeExpression, "Equals", null, target);
Expression<Func<TView, bool>> textFilterExpression =
Expression.Lambda<Func<TView, bool>>(containsMethod, parameter);
return textFilterExpression;
}
private Expression<Func<TView, bool>> GetTextFilterExpression(string fieldName, string filter)
{
ParameterExpression parameter = Expression.Parameter(typeof(TView), "x");
Expression property = Expression.Property(parameter, fieldName);
Expression target = Expression.Constant(filter.ToUpper());
Expression containsMethod = Expression.Call(property, "Contains", null, target);
Expression<Func<TView, bool>> textFilterExpression =
Expression.Lambda<Func<TView, bool>>(containsMethod, parameter);
return textFilterExpression;
}
Thanks for any suggestion.
I've tried to implement this using Expression Trees, and got it to get two expressions, but now I didn't get to combine these expressions to build one to pass to the final
First, you need to declare a parameter for your final (outer) lambda. Then you need to invoke your two filter (inner) lambdas independently, passing in the same argument to each:
// using E = System.Linq.Expressions.Expression;
var item = E.Parameter(typeof(TView));
var combined = E.Lambda<Func<TView, bool>>(
E.AndAlso(
E.Invoke(parentCodeFilterExpression, item),
E.Invoke(textFilterExpression, item)),
item);
If you need these expressions to be compatible with a query provider like Entity Framework, things get a bit messier because Invoke expressions probably aren't supported. You'll have to manually inline the two filter lambdas, which requires walking each filter's body and replacing the inner parameter references with references to the outer lambda's parameter:
// using E = System.Linq.Expressions.Expression;
sealed class ParameterReplacementVisitor : ExpressionVisitor
{
private readonly IDictionary<E, E> _replacements;
public ParameterReplacementVisitor(IDictionary<E, E> replacements)
{
_replacements = replacements;
}
protected override Expression VisitParameter(ParameterExpression node)
{
E replacement;
if (_replacements.TryGetValue(node, out replacement))
return this.Visit(replacement);
return base.VisitParameter(node);
}
}
// ...
var item = E.Parameter(typeof(TView));
var visitor = new ParameterReplacementVisitor(
new Dictionary<E, E> {
{ parentCodeFilterExpression.Parameters[0], item },
{ textFilterExpression.Parameters[0], item }
}
);
var combined = E.Lambda<Func<TView, bool>>(
E.AndAlso(
visitor.Visit(parentCodeFilterExpression.Body),
visitor.Visit(textFilterExpression.Body)),
item);
Alternatively, if you are composing the inner expressions in a closed environment as your post suggests, you could simply pass the outer lambda parameter as an argument to the methods that construct the inner expressions, and return just the bodies (don't bother wrapping the inner filters in lambdas).
I think you need something like this:
MethodCallExpression where = Expression.Call((
typeof(Queryable),
"Where",
new Type[] { TView },
lambdaExpression );
Please note that I don't consider this a solution; it's merely an idea or example. Maybe this link will help you out.
You can compile an Expression<TDelegate> into a TDelegate by using the Compile method on the Expression:
Expression<Func<TView, bool>> lambdaExpression =
GetLambdaExpressionForSearchByField(fieldName, filter, parentCode);
Func<TView, bool> func = lambdaExpression.Compile();
Once you have that you can use it as a parameter for the Where function.
With the code above you can then use
return new PersistenciaImpl<TView>().Where(func).ToList();

Categories

Resources