Dynamically generate LINQ (member-)expressions - c#

I am using FluentValidation to validate my form items in Xamarin.Forms. The definition of these items comes from the outside. Therefore I don't know, what properties I need to validate on my viewmodel.
RuleFor(viewmodel => viewmodel.Description).NotEmpty();
My idea was, to dynamically generate these expressions at runtime.
I created a List in the validator to store these expressions.
public List<Expression<Func<IViewModel, object>>> RequiredFieldExpressions
= new List<Expression<Func<IViewModel, object>>>();
Before validating my viewmodel, I generate the expressions.
var tmpMethod = typeof(TypeHelper).GetRuntimeMethod("GetExpression", new Type[] { typeof(string) });
var tmpGeneric = tmpMethod.MakeGenericMethod(myViewModel.GetType(), typeof(string));
var tmpInvokeResult = tmpGeneric.Invoke(null, new object[] {coreObjectPropertyName});
The method which creates the expression:
public static Expression<Func<T, TProperty>> GetExpression<T, TProperty>(string inPropertyName) where T : IViewModel
{
var tmpPropertyInfo = typeof(T).GetRuntimeProperties().First(p => p.Name == inPropertyName);
var tmpEntityParam = Expression.Parameter(typeof(T), "type");
Expression tmpExpression = Expression.Property(tmpEntityParam, tmpPropertyInfo);
if (tmpPropertyInfo.PropertyType != typeof(TProperty))
{
tmpExpression = Expression.Convert(tmpExpression, typeof(TProperty));
}
return Expression.Lambda<Func<T, TProperty>>(tmpExpression, tmpEntityParam);
}
Now the line which should create the validation rule throws an invalid cast exception.
// Cast not valid
RuleFor((Expression<Func<IViewModel, object>>) tmpInvokeResult).NotEmpty();
What am I missing?

I had to change the method call from
var tmpGeneric = tmpMethod.MakeGenericMethod(myViewModel.GetType(), typeof(string));
to
var tmpGeneric = tmpMethod.MakeGenericMethod(myViewModel.GetType(), typeof(object));
My guess
Xamarin.Forms uses a portable class library (PCL). It seems like the functionality to cast generic expressions isn't implemented.
It would be nice if somebody could verify this.
Update
I am not able to cast generic expressions. Seems like it's just not possible. You need to explicitly convert the expression before returning it.
https://dotnetfiddle.net/ufNId4

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.

Using Reflection on Type T to create Expression<Func<T, TValue>> for Properties that have attributes

I'm currently exploring the viability to encapsulate some logic that would typically be iterative. Using a 3rd party library it has a generic Html Helper method that allows you to map properties from a Generic T class to a table component. Typically you'd have to write something to the effect of:
HtmlHelper.GenerateTable<ClassName>
.configure(tableDefinition =>{
// Adding each property you want to render here
tableDefinition.add(myClassRef => myClassRef.propertyA);
tableDefinition.add(myClassRef => myClassRef.propertyB);
})
I'm exploring the idea of adding attributes to properties such as a standard Display attribute and then using reflection, adding that property to the container. The add method only accepts an argument of type Expression<Func<T, TValue>>. With my current understanding of reflection I know I can property identify properties that would be applicable by looping through PropertyInfo and checking for the attribute I want using GetCustomAttribute.
What I'm stumped on is if it's possible using reflection to supply the argument type that the add method expects?
I'll throw the helper method logic I've started using so far. My assumption is that this is leading me towards Expression and Lambda classes, but I haven't been able to flesh out anything that works since I don't technically have a TValue.
var t = typeof(T);
List<PropertyInfo> properties = t.GetProperties().ToList();
foreach(var prop in properties)
{
var attr = (DisplayAttribute[])prop.GetCustomAttributes(typeof(DisplayAttribute), false);
if (attr.Length > 0)
{
// TODO: Call add with expression?
}
}
You can achieve this fairly easily with the methods on the Expression class. To start with, it's easiest to write expressions using lambdas (e.g. Expression<Func<A, B>> expr = x => x.PropertyA), then inspect it in a debugger to see what the compiler constructs.
In your case, something like this should work:
// The parameter passed into the expression (myClassRef) in your code
var parameter = Expression.Parameter(typeof(T), "myClassRef");
// Access the property described by the PropertyInfo 'prop' on the
// myClassRef parameter
var propertyAccess = Expression.Property(parameter, prop);
// Since we're returning an 'object', we'll need to make sure we box value types.
var box = Expression.Convert(propertyAccess, typeof(object));
// Construct the whole lambda
var lambda = Expression.Lambda<Func<T, object>>(box, parameter);
tableDefinition.add(lambda);
Note that I'm passing an Expression<Func<T, object>> into add, rather than an Expression<Func<T, TValue>>. I'm guessing that this doesn't matter, and it avoids having to call add using reflection. When I've written methods similar to add in the past, I don't care about TValue at all: I just inspect the Expression and fetch the PropertyInfo from the property access.
If you do need to pass an Expression<Func<T, TValue>> you'll have to do something like:
var delegateType = typeof(Func<,>).MakeGenericType(typeof(T), prop.PropertyType);
var lambda = Expression.Lambda(delegateType, propertyAccess, parameter);
// I'm assuming that your `add` method looks like:
// void add<T, TValue>(Expression<Func<T, TValue>> expr)
// I'm also assuming there's only one method called 'add' -- be smarter in that
// 'GetMethod' call if not.
var addMethod = typeof(TableDefinition)
.GetMethod("add")
.MakeGenericMethod(typeof(T), prop.PropertyType);
addMethod.Invoke(tableDefinition, new object[] { lambda });
Note that you don't need the Expression.Convert(..., typeof(object)) in this case.

C# Compiled expression to create new instance of T and copy values to its properties from another instance of T

I would like to know if it is possible to create an instance of a type known only at runtime and assign values to the properties of this instance by using compiled expressions and if so how it is to be done.
I have a generic class with a method which accepts an instance of T and returns a copy. T is only known at runtime or rather is user/consumer defined. I know how to do so with reflection (assuming it has an empty constructor in this example and without exception handling or null checks for simplification).
public class MyClass<T>
{
public T CreateCopy(T source)
{
var type = typeof(T);
var copy = type.GetConstructor(Type.EmptyTypes).Invoke(null);
foreach(var pi in type.GetProperties())
{
pi.SetValue(copy, pi.GetValue(source));
}
return copy;
}
}
Reflection is quite expensive and after some digging i found an option to at least create an instance of T with compiled expressions.
var type = typeof(T);
Expression.Lambda<Func<T>>(Expression
.New(type.GetConstructor(Type.EmptyTypes)
?? throw new InvalidOperationException(
$"Type has to have an empty public constructor. {type.Name}")))
.Compile();
After some benchmarking i have found out that it performs around 6 times faster than the CreateCopy(...) method. The thing is that i do not know which type will be passed in as a generic and how many properties it will have.
Is there a way to do all of the operations from CreateCopy(...) method with compiled expressions?
Ihave looked into Expression.Asign, Expression.MemberInit but i am not able to find anything appropriate. The problem with Expression.MemberInit ist that it expects to have an Expresssion.Bind and Expression.Constant but i cant get the values of the properties from the passed instance of T into it. Is there a way?
Thank you.
P.S. So i am looking for something like:
var type = typeof(T);
var propertyInfos = type.GetProperties();
var ctor = Expression.New(type.GetConstructor(Type.EmptyTypes));
var e = Expression.Lambda<Func<T, T>>(Expression
.MemberInit(ctor, propertyInfos.Select(pi =>
Expression.Bind(pi, Expression.Constant(pi.GetValue(source)))))).Compile();
You are almost there. What you need is to define a parameter and then assign the properties with property access expression like below :
public static T Express<T>(T source)
{
var parameter = Expression.Parameter(typeof(T), "source");
var type = typeof(T);
var ctor = Expression
.New(type.GetConstructor(Type.EmptyTypes));
var propertyInfos = type.GetProperties();
var e = Expression
.Lambda<Func<T, T>>(
Expression
.MemberInit(ctor, propertyInfos.Select(pi =>
Expression.Bind(pi, CanBeAssigned(pi.PropertyType)
? (Expression)Expression.Property(parameter, pi.Name)
: Expression.Call(typeof(Program).GetMethod(nameof(Express))
.MakeGenericMethod(pi.PropertyType),
Expression.Property(parameter, pi.Name)
))
)),
parameter
);
var x = e.Compile();
var z = x(source);
return z;
}
public static bool CanBeAssigned(Type t)
{
return t.IsValueType || t.Name == "String"; // this might need some improvements
}

Getting data from a string defined table name filtered on a string defined field

I am trying to return the contents of a Where filtered table of dynamic type based on a dynamic field name from an array of values.
Heres what I have so far:
public JsonResult GetRelationShips(string linkingTable, string idField, int[] ids)
{
var tableType = typeof(context).GetProperty(linkingTable);
var entityTable = tableType.GetValue(db) as IQueryable;
var method = typeof(List<int>).GetMethod("Contains");
var eParam = Expression.Parameter(tableType.PropertyType.GetGenericArguments()[0]);
var call = Expression.Call(Expression.Constant(ids.ToList()), method, Expression.Property(eParam, idField));
var func = typeof(Func<,>);
var genericFunc = func.MakeGenericType(tableType.PropertyType.GetGenericArguments()[0], typeof(bool));
var lambda = Expression.Lambda(genericFunc, call, eParam);
var results = typeof(System.Linq.Enumerable).GetMethods().Where(x => x.Name == "Where").First().Invoke(db, new object[] { lambda });
return Json(results);
}
That last line is giving me an error:
Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true.
Honestly, I cobbled this together this afternoon from snippets from all over the internet. I have no idea what I'm doing here, this is new to me and I'm keen to learn. Just trying to avoid SQL injection, the rest of the project is entirely Linq so I'm soldiering on. I'm learning Generic types too, got my head around that, don't see how I can use them here though.
There any many flaws in this single line of code:
var results = typeof(System.Linq.Enumerable).GetMethods().Where(x => x.Name == "Where").First().Invoke(db, new object[] { lambda });
Trying to call Enumerable.Where instead of Queryable.Where. This would cause retrieving the whole table data and performing the filtering in memory instead of the database side.
Trying to call potentially wrong method. Where has 2 overloads, and it's undefined which one will be returned as first by the reflection.
Trying to invoke generic method definition, causing the exception you are getting. You have to first construct a generic method by using MakeGenericMethod and invoke that.
Trying to invoke static generic extension method via reflection as if it is instance method. Instead, you should pass null as first argument to Invoke and pass new object[] { entityTable, lambda } as second argument.
You can avoid all these traps by simply using the C# dynamic method dispatch:
IQueryable results = Queryable.Where((dynamic)entityTable, (dynamic)lambda);
The whole code can be simplified by using the following Expression.Call overload:
public static MethodCallExpression Call(
Type type,
string methodName,
Type[] typeArguments,
params Expression[] arguments);
which is very useful for "calling" static generic extension methods:
var query = (IQueryable)db.GetType().GetProperty(linkingTable).GetValue(db);
// e =>
var entity = Expression.Parameter(query.ElementType, "e");
// ids.Contains(e.idField)
// = Enumerable<int>.Contains(ids, e.idField)
var containsCall = Expression.Call(
typeof(Enumerable),
nameof(Enumerable.Contains),
new Type[] { typeof(int) },
Expression.Constant(ids),
Expression.Property(entity, idField)
);
// e => ids.Contains(e.idField)
var predicate = Expression.Lambda(containsCall, entity);
// query = query.Where(predicate);
query = Queryable.Where((dynamic)query, (dynamic)predicate);
You can also avoid the dynamic Where call and use similar Expression.Call based approach to "call" it, combined with IQueryProvider.CreateQuery:
// query.Where(predicate)
// = Queryable.Where<ElementType>(query, predicate)
var whereCall = Expression.Call(
typeof(Queryable),
nameof(Queryable.Where),
new Type[] { query.ElementType },
query.Expression,
predicate
);
// query = query.Where(predicate)
query = query.Provider.CreateQuery(whereCall);
I've provided all that just because you said you are keen to learn. The simplest way of handling such tasks (and not reinvent the wheel) is to use some 3rd party package. For instance, with System.Linq.Dynamic package the whole code would be:
var query = ((IQueryable)db.GetType().GetProperty(linkingTable).GetValue(db))
.Where($"#0.Contains({idField})", ids);

Dynamic lambda expression (OrderBy) and nullable property type

I'm trying to dynamically create expression that will sort data from database through Entity Framework. But I encountered one problem and cannot overcome it. Maybe let me explain what I'm trying to do. My goal is to create expression like this:
x => x.Property
Where "Property" is the name of property which I'd like to specify dynamically.
Now, let's go to the class, which represents the table in database (I simplified it to make things more clear):
public class MyModelClass
{
public long MyLongProperty { get; set; }
public decimal? MyNullableDecimalProperty { get; set; } // IMPORTANT: it's nullable
}
It is my code, where I'm trying to create expression described earlier:
// Db is EntityFramework context
IQueryable<MyModelClass> list = Db.MyModels.Select(x => x);
// x =>
var argument = Expression.Parameter(list.ElementType, "x");
// x.MyNullableDecimalProperty
var propertyToOrder = Expression.Property(argument, "MyNullableDecimalProperty");
// x => x.MyNullableDecimalProperty
var finalExpression = Expression.Call(
typeof (Queryable),
"OrderBy",
new[] { list.ElementType, typeof(IComparable) },
list.Expression,
Expression.Lambda<Func<MyModelClass, IComparable>>(propertyToOrder, argument));
list = list.Provider.CreateQuery<MyModelClass>(finalExpression);
Problem occurs at 4th statement (var finalExpression = Expression.Call(...)). I get an exception:
Expression of type „System.Nullable`1[System.Decimal]” cannot be used for return type „System.IComparable”.
As far I understand the problem is with me using "IComparable" type where "MyNullableDecimalProperty" is Nullable and Nullable doesn't user IComparable interface. The exception isn't thrown when I'm ordering by "MyLongProperty" or when I replace "IComparable".
So my questions:
What type should I use to make it work with any nullable properties?
Is it possible to use one type and it will work with all properties whether they are nullable or non-nullable.
Notice: I know I can use for ex. Dynamic Linq library, but I'm not interested in this solution - I'd like to learn how to overcome it without using 3rd party libraries.
There's no reason to use IComparable. Indeed, many types that are comparable do not implement IComparable. Simply use the runtime type of whatever you're passing:
var finalExpression = Expression.Call(
typeof (Queryable),
"OrderBy",
new[] { list.ElementType, propertyToOrder.Type },
list.Expression,
Expression.Lambda(propertyToOrder, new [] { argument }));
You don't need to specify the IComparable part, and you can also use the Queryable.OrderBy / OrderByDescending methods to help you here:
IQueryable<TSource> source = .....
var sourceType = typeof(TSource);
var parameter = Expression.Parameter(sourceType, "item");
var propertyInfo = GetProperty(sourceType, propertyName);
var orderByProperty = Expression.Property(parameter, propertyInfo);
orderBy = Expression.Lambda(orderByProperty, new[] { parameter });
return Queryable.OrderBy(source, (dynamic)orderBy)
Give that a go and see how you get on, I'm pretty sure this will work for native types and nullable types.

Categories

Resources