How to build expression tree from string values - c#

I've asked a specific question elsewhere, but after no response and some investigating I've got it down to something much more generic, but I'm still struggling to build an expression tree.
I'm using a third party library which does some mappings using an interface and extension methods. Those mappings are specified as an expression tree, what I want to do is build that expression tree up from string values.
The extension method signature:
public static T UpdateGraph<T>(this DbContext context, T entity, Expression<Func<IUpdateConfiguration<T>, object>> mapping = null, bool allowDelete = true) where T : class, new();
The interface IUpdateConfiguration is just a marker interface, but has the following extension methods:
public static class UpdateConfigurationExtensions
{
public static IUpdateConfiguration<T> OwnedCollection<T, T2>(this IUpdateConfiguration<T> config, Expression<Func<T, ICollection<T2>>> expression);
public static IUpdateConfiguration<T> OwnedCollection<T, T2>(this IUpdateConfiguration<T> config, Expression<Func<T, ICollection<T2>>> expression, Expression<Func<IUpdateConfiguration<T2>, object>> mapping);
public static IUpdateConfiguration<T> OwnedEntity<T, T2>(this IUpdateConfiguration<T> config, Expression<Func<T, T2>> expression);
public static IUpdateConfiguration<T> OwnedEntity<T, T2>(this IUpdateConfiguration<T> config, Expression<Func<T, T2>> expression, Expression<Func<IUpdateConfiguration<T2>, object>> mapping);
}
Using an example entity:
public class Person
{
public Car Car {get;set;}
public House House {get;set;}
}
So normal explicit usage is:
dbContext.UpdateGraph(person, mapping => mapping.OwnedEntity(p => p.House).OwnedEntity(p=> p.Car));
What I need to do is build up that mapping from a list of property names,
var props = {"Car","House"}
dbContext.UpdateGraph(person, buildExpressionFromStrings<Person>(props);
I've got so far:
static Expression<Func<IUpdateConfiguration<t>, object>> buildExpressionFromStrings<t>(IEnumerable<string> props)
{
foreach (var s in props)
{
var single = buildExpressionFromString(s);
somehow add this to chaining overall expression
}
}
static Expression<Func<IUpdateConfiguration<t>, object>> buildExpressionFromString<t>(string prop)
{
var ownedChildParam = Expression.Parameter(typeof(t));
var ownedChildExpression = Expression.PropertyOrField(ownedChildParam, prop);
var ownedChildLam = Expression.Lambda(ownedChildExpression, ownedChildParam);
// Up to here I think we've built the (o => o.Car) part of map => map.OwnedEntity(o => o.Car)
// So now we need to build the map=>map.OwnedEntity(ownedChildLam) part, by calling Expression.Call I believe, but here I'm getting confused.
}
In reality, the real-world code is more complex than this (needs to deal with recursion and child properties/mappings), but I think I can get that sorted once I get the expression built for one level. I've been tearing my hair out for over a day, trying to get this sorted... To give some context, I'm using entity framework, and some configuration to define aggregate roots.

Few things to mention. Here
mapping => mapping.OwnedEntity(p => p.House).OwnedEntity(p=> p.Car)
the parts of the lambda expression body are not lambda expressions, but just chained method call expressions, the first using the lambda expression parameter and the next using the previous result.
Second, the extension method are just static method call C# sugar, and in expression trees they must be "called" as static methods.
So, building a call to
public static IUpdateConfiguration<T> OwnedEntity<T, T2>(this IUpdateConfiguration<T> config, Expression<Func<T, T2>> expression);
could be like this
static Expression BuildConfigurationCall<T>(Expression config, string propertyName)
{
var parameter = Expression.Parameter(typeof(T), "it");
var property = Expression.Property(parameter, propertyName);
var selector = Expression.Lambda(property, parameter);
return Expression.Call(
typeof(UpdateConfigurationExtensions),
nameof(UpdateConfigurationExtensions.OwnedEntity),
new [] { typeof(T), property.Type },
config,
selector);
}
and the lambda expression in question would be:
static Expression<Func<IUpdateConfiguration<T>, object>> BuildConfigurationExpression<T>(IEnumerable<string> propertyNames)
{
var parameter = Expression.Parameter(typeof(IUpdateConfiguration<T>), "config");
var body = propertyNames.Aggregate((Expression)parameter,
(config, propertyName) => BuildConfigurationCall<T>(config, propertyName));
return Expression.Lambda<Func<IUpdateConfiguration<T>, object>>(body, parameter);
}

I use Object Builder for dynamic object creating with given props. And I use DynamicQueryable for string based expressions.

Related

When using a Lambda Expression to perform an OrderBy on a generic collection, integer properties cause an exception [duplicate]

I'm trying to achive this conversion
"Address.Street" => (p) => p.Address.Street
"Name" => (p) => p.Name
I was able to find a method to generate an order by expression using reflection but it won't work for complex sort as Address.Street since works for a single property level.
Is there a way to do this? I've seen that I compile lambda expressions but I couldn't understand how to make it work for this case.
Creating an expression is not hard, but the tricky part is how to bind it to the corresponding OrderBy(Descending) / ThenBy(Descendig) methods when you don't know the type of the property (hence the type of the selector expression result).
Here is all that encapsulated in a custom extension method:
public static partial class QueryableExtensions
{
public static IOrderedQueryable<T> OrderByMember<T>(this IQueryable<T> source, string memberPath)
{
return source.OrderByMemberUsing(memberPath, "OrderBy");
}
public static IOrderedQueryable<T> OrderByMemberDescending<T>(this IQueryable<T> source, string memberPath)
{
return source.OrderByMemberUsing(memberPath, "OrderByDescending");
}
public static IOrderedQueryable<T> ThenByMember<T>(this IOrderedQueryable<T> source, string memberPath)
{
return source.OrderByMemberUsing(memberPath, "ThenBy");
}
public static IOrderedQueryable<T> ThenByMemberDescending<T>(this IOrderedQueryable<T> source, string memberPath)
{
return source.OrderByMemberUsing(memberPath, "ThenByDescending");
}
private static IOrderedQueryable<T> OrderByMemberUsing<T>(this IQueryable<T> source, string memberPath, string method)
{
var parameter = Expression.Parameter(typeof(T), "item");
var member = memberPath.Split('.')
.Aggregate((Expression)parameter, Expression.PropertyOrField);
var keySelector = Expression.Lambda(member, parameter);
var methodCall = Expression.Call(
typeof(Queryable), method, new[] { parameter.Type, member.Type },
source.Expression, Expression.Quote(keySelector));
return (IOrderedQueryable<T>)source.Provider.CreateQuery(methodCall);
}

Lambda expression inside another lambda expression [duplicate]

I have an existing expression of type Expression<Func<T, object>>; it contains values like cust => cust.Name.
I also have a parent class with a field of type T. I need a method that accepts the above as a parameter and generates a new expression that takes the parent class (TModel) as a parameter. This will be used as an expression parameter of an MVC method.
Thus, cust => cust.Name becomes parent => parent.Customer.Name.
Likewise, cust => cust.Address.State becomes parent => parent.Customer.Address.State.
Here's my initial version:
//note: the FieldDefinition object contains the first expression
//described above, plus the MemberInfo object for the property/field
//in question
public Expression<Func<TModel, object>> ExpressionFromField<TModel>(FieldDefinition<T> field)
where TModel: BaseModel<T>
{
var param = Expression.Parameter(typeof(TModel), "t");
//Note in the next line "nameof(SelectedItem)". This is a reference
//to the property in TModel that contains the instance from which
//to retrieve the value. It is unqualified because this method
//resides within TModel.
var body = Expression.PropertyOrField(param, nameof(SelectedItem));
var member = Expression.MakeMemberAccess(body, field.Member);
return Expression.Lambda<Func<TModel, object>>(member, param);
}
The error I'm currently receiving is when I have a field with multiple parts (i.e. cust.Address.State instead of just cust.Name). I get an error on the var member line that the specified member doesn't exist--which is true, since the body at that refers to the parent's child (Customer) and not the item that contains the member (Address).
Here's what I wish I could do:
public Expression<Func<TModel, object>> ExpressionFromField<TModel>(FieldDefinition<T> field)
where TModel: BaseModel<T>
{
var param = Expression.Parameter(typeof(TModel), "t");
var body = Expression.PropertyOrField(param, nameof(SelectedItem));
var IWantThis = Expression.ApplyExpressionToField(field.Expression, body);
return Expression.Lambda<Func<TModel, object>>(IWantThis, param);
}
Any help getting to this point would be greatly appreciated.
Edit: This was marked as a possible duplicate of this question; however, the only real similarity is the solution (which is, in fact, the same). Composing expressions is not an intuitive solution to accessing nested properties via expressions (unless one's inuition is guided by certain experience, which should not be assumed). I also edited the question to note that the solution needs to be suitable for a paramter of an MVC method, which limits the possible solutions.
What you're looking for is the ability to compose expressions, just as you can compose functions:
public static Expression<Func<T, TResult>> Compose<T, TIntermediate, TResult>(
this Expression<Func<T, TIntermediate>> first,
Expression<Func<TIntermediate, TResult>> second)
{
return Expression.Lambda<Func<T, TResult>>(
second.Body.Replace(second.Parameters[0], first.Body),
first.Parameters[0]);
}
This relies on the following method to replace all instances of one expression with another:
public class ReplaceVisitor:ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression ex)
{
if(ex == from) return to;
else return base.Visit(ex);
}
}
public static Expression Replace(this Expression ex,
Expression from,
Expression to)
{
return new ReplaceVisitor(from, to).Visit(ex);
}
You can now take an expression selecting a property:
Expression<Func<Customer, object>> propertySelector = cust => cust.Name;
And an expression selecting that object from the model:
Expression<Func<CustomerModel, Customer>> modelSelector = model => model.Customer;
and compose them:
Expression<Func<Customer, object> magic = modelSelector.Compose(propertySelector);

Returning filtered IQueryable<T> with anonymous types

I have a set of Reports which I need to perform filtering on before returning the output. I would like to perform this with a single anonymous method to avoid duplicating the same code in different repositories. I'm using Entity Framework so the model types all related to the database and inherit from a base class called ReportBase.
This is what how I currently implement the filtering, each report type has to implement this method with a different context and returning a different IQueryable type.
private IQueryable<ReviewAgreement> GetFiltered(ReportFilter filter)
{
IQueryable<ReviewAgreement> reviewAgreementQueryable = Context.ReviewAgreements.Where(p => p.ClientWorkflowId == filter.ClientWorkflowId);
if (filter.AppraisalLevelId.HasValue)
{
reviewAgreementQueryable = reviewAgreementQueryable.Where(p => p.AppraisalLevelId == filter.AppraisalLevelId.Value);
}
return reviewAgreementQueryable;
}
I've been trying to implement this anonymously so I can reuse it, as in this non functional example.
public IQueryable<T> GetFiltered(ReportFilter filter)
{
IQueryable<T> reportQueryable = Context.Set<T>();
reportQueryable = reportQueryable.Where(p => p.ClientWorkflowId == filter.ClientWorkflowId);
if (filter.AppraisalLevelId.HasValue)
{
reportQueryable = reportQueryable.Where(p => p.AppraisalLevelId == filter.AppraisalLevelId.Value);
}
return reportQueryable;
}
The issue I am having is of course that the use of Where is ambiguous, so it cannot resolve p.ClientWorkflowId.
I have tried using a Func<T, TResult> delegate to pass in the filtering options this but the Where operation seems to want to return a list.
Is there actually a method I can use to achieve the effect I want?
Declare an interface that has the two ID properties that you need to perform this operation.
Ensure that your entities implement that interface.
Add a constraint to the generic argument that it implements that interface.
Note that if your base class defines both of the properties in question then you don't need an interface, and can simply constrain the type to that base class:
public IQueryable<T> GetFiltered<T>(ReportFilter filter) where T : ReportBase
{
// body unchanged
}
If you want to go down the route of accepting parameters to represent these properties than it's also possible. The first thing is that you'll need to accept expressions, not Func objects, so that the query provider can analyze them. This means changing the function signature to:
public IQueryable<T> GetFiltered<T>(ReportFilter filter,
Expression<Func<T, int>> clientIdSelector,
Expression<Func<T, int>> appraisalIdSelector)
{
Next, to turn these selectors into predicates that compare the value to an ID that we have is a bit more involved for expressions than it is for regular delegates. What we really need here is a Compose method; for delegates it's simple enough, to compose one method with another you just invoke it with the parameter being the result of the first. With expressions this means taking the body of one expression and replacing all instance of the parameter with the body of another, and then wrapping the whole thing up in a new Lambda.
public static Expression<Func<TFirstParam, TResult>>
Compose<TFirstParam, TIntermediate, TResult>(
this Expression<Func<TFirstParam, TIntermediate>> first,
Expression<Func<TIntermediate, TResult>> second)
{
var param = Expression.Parameter(typeof(TFirstParam), "param");
var newFirst = first.Body.Replace(first.Parameters[0], param);
var newSecond = second.Body.Replace(second.Parameters[0], newFirst);
return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}
This itself is dependent on the ability to replace all instances of one expression with another. To do that we'll need to use the following:
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
internal class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
Now that we have all of this in place we can actually compose our selectors with a comparison to the ID values of the filter:
IQueryable<T> reportQueryable = Context.Set<T>();
reportQueryable = reportQueryable
.Where(clientIdSelector.Compose(id => id == filter.ClientWorkflowId));
if (filter.AppraisalLevelId.HasValue)
{
reportQueryable = reportQueryable
.Where(clientIdSelector.Compose(id => id == filter.AppraisalLevelId.Value));
}
return reportQueryable;

AutoMapper for Func's between selector types

I have two types: Cat and Dog. I'd like to select Cats using a Func<Dog, bool>. To do that, I need a way to map the properties from Cat to Dog in some sort of mapper (similar to how AutoMapper maps properties from one object to another type of object).
I'm imagining something like this:
public Cat GetCat(Func<Dog, bool> selector)
{
Func<Cat, bool> mappedSelector = getMappedSelector(selector);
return _catRepository.Get(mappedSelector);
}
private Func<Cat, bool> getMappedSelector(Func<Dog, bool> selector)
{
//some code here to map from one function type to another
//something like AutoMapper would be sweet...
//something that I can configure how I want the properties to be mapped.
}
Either there's already something that does this or there should be.
Here's a solution using AutoMapper:
Func<Cat, bool> GetMappedSelector(Func<Dog, bool> selector)
{
Func<Cat, Dog> mapper = Mapper.CreateMapExpression<Cat, Dog>().Compile();
Func<Cat, bool> mappedSelector = cat => selector(mapper(cat));
return mappedSelector;
}
UPDATE: It's been 1.5 years since I first answered this, and I figured I'd expand on my answer now since people are asking how to do this when you have an expression as opposed to a delegate.
The solution is the same in principle - we need to be able to compose the two functions (selector and mapper) into a single function. Unfortunately, since there's no way in C# to "call" one expression from another (like we could with delegates), we can't directly represent this in code. For example, the following code will fail to compile:
Expression<Func<Cat, bool>> GetMappedSelector(Expression<Func<Dog, bool>> selector)
{
Expression<Func<Cat, Dog>> mapper = Mapper.CreateMapExpression<Cat, Dog>();
Expression<Func<Cat, bool>> mappedSelector = cat => selector(mapper(cat));
return mappedSelector;
}
The only way to create our composed function, therefore, is to build up the expression tree ourselves using the System.Linq.Expressions classes.
What we really need to do is to modify the body of the selector function so that all instances of its parameter are replaced by the body of the mapper function. This will become the body of our new function, which will accept mapper's parameter.
To replace the parameter I created a subclass of ExpressionVisitor class that can traverse an expression tree and replace a single parameter with an arbitrary expression:
class ParameterReplacer : ExpressionVisitor
{
private ParameterExpression _parameter;
private Expression _replacement;
private ParameterReplacer(ParameterExpression parameter, Expression replacement)
{
_parameter = parameter;
_replacement = replacement;
}
public static Expression Replace(Expression expression, ParameterExpression parameter, Expression replacement)
{
return new ParameterReplacer(parameter, replacement).Visit(expression);
}
protected override Expression VisitParameter(ParameterExpression parameter)
{
if (parameter == _parameter)
{
return _replacement;
}
return base.VisitParameter(parameter);
}
}
Then I created an extension method, Compose(), that uses the visitor to compose two lambda expressions, an outer and an inner:
public static class FunctionCompositionExtensions
{
public static Expression<Func<X, Y>> Compose<X, Y, Z>(this Expression<Func<Z, Y>> outer, Expression<Func<X, Z>> inner)
{
return Expression.Lambda<Func<X ,Y>>(
ParameterReplacer.Replace(outer.Body, outer.Parameters[0], inner.Body),
inner.Parameters[0]);
}
}
Now, with all that infrastructure in place, we can modify our GetMappedSelector() method to use our Compose() extension:
Expression<Func<Cat, bool>> GetMappedSelector(Expression<Func<Dog, bool>> selector)
{
Expression<Func<Cat, Dog>> mapper = Mapper.CreateMapExpression<Cat, Dog>();
Expression<Func<Cat, bool>> mappedSelector = selector.Compose(mapper);
return mappedSelector;
}
I created a simple console application to test this out. Hopefully, my explanation was not too obfuscated; but unfortunately, there isn't really a simpler approach to doing what you're trying to do. If you are still totally confused, at least you can reuse my code and have gained an appreciation for the nuances and complexities of dealing with expression trees!
#luksan, thanks for the inspiration! Your solution didn't solve my problem, but got me thinking. Since I needed to pass the translated expression to IQueryable.OrderBy(), using the inside-expression translation approach didn't work. But I came up with a solution that will work on both cases and is also simpler to implement. It's also generic so can be reused for any mapped types. Here is the code:
private Expression<Func<TDestination, TProperty>> GetMappedSelector<TSource, TDestination, TProperty>(Expression<Func<TSource, TProperty>> selector)
{
var map = Mapper.FindTypeMapFor<TSource, TDestination>();
var mInfo = ReflectionHelper.GetMemberInfo(selector);
if (mInfo == null)
{
throw new Exception(string.Format(
"Can't get PropertyMap. \"{0}\" is not a member expression", selector));
}
PropertyMap propmap = map
.GetPropertyMaps()
.SingleOrDefault(m =>
m.SourceMember != null &&
m.SourceMember.MetadataToken == mInfo.MetadataToken);
if (propmap == null)
{
throw new Exception(
string.Format(
"Can't map selector. Could not find a PropertyMap for {0}", selector.GetPropertyName()));
}
var param = Expression.Parameter(typeof(TDestination));
var body = Expression.MakeMemberAccess(param, propmap.DestinationProperty.MemberInfo);
var lambda = Expression.Lambda<Func<TDestination, TProperty>>(body, param);
return lambda;
}
Here is the ReflectionHelper code (used just to keep the code above cleaner)
private static class ReflectionHelper
{
public static MemberInfo GetMemberInfo(Expression memberExpression)
{
var memberExpr = memberExpression as MemberExpression;
if (memberExpr == null && memberExpression is LambdaExpression)
{
memberExpr = (memberExpression as LambdaExpression).Body as MemberExpression;
}
return memberExpr != null ? memberExpr.Member : null;
}
}

C# - code to order by a property using the property name as a string [duplicate]

This question already has answers here:
Dynamic LINQ OrderBy on IEnumerable<T> / IQueryable<T>
(24 answers)
Closed 1 year ago.
What's the simplest way to code against a property in C# when I have the property name as a string? For example, I want to allow the user to order some search results by a property of their choice (using LINQ). They will choose the "order by" property in the UI - as a string value of course. Is there a way to use that string directly as a property of the linq query, without having to use conditional logic (if/else, switch) to map the strings to properties. Reflection?
Logically, this is what I'd like to do:
query = query.OrderBy(x => x."ProductId");
Update:
I did not originally specify that I'm using Linq to Entities - it appears that reflection (at least the GetProperty, GetValue approach) does not translate to L2E.
I would offer this alternative to what everyone else has posted.
System.Reflection.PropertyInfo prop = typeof(YourType).GetProperty("PropertyName");
query = query.OrderBy(x => prop.GetValue(x, null));
This avoids repeated calls to the reflection API for obtaining the property. Now the only repeated call is obtaining the value.
However
I would advocate using a PropertyDescriptor instead, as this will allow for custom TypeDescriptors to be assigned to your type, making it possible to have lightweight operations for retrieving properties and values. In the absence of a custom descriptor it will fall back to reflection anyhow.
PropertyDescriptor prop = TypeDescriptor.GetProperties(typeof(YourType)).Find("PropertyName");
query = query.OrderBy(x => prop.GetValue(x));
As for speeding it up, check out Marc Gravel's HyperDescriptor project on CodeProject. I've used this with great success; it's a life saver for high-performance data binding and dynamic property operations on business objects.
I'm a little late to the party, however, I hope this can be of some help.
The problem with using reflection is that the resulting Expression Tree will almost certainly not be supported by any Linq providers other than the internal .Net provider. This is fine for internal collections, however this will not work where the sorting is to be done at source (be that SQL, MongoDb, etc.) prior to pagination.
The code sample below provides IQueryable extention methods for OrderBy and OrderByDescending, and can be used like so:
query = query.OrderBy("ProductId");
Extension Method:
public static class IQueryableExtensions
{
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string propertyName)
{
return source.OrderBy(ToLambda<T>(propertyName));
}
public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string propertyName)
{
return source.OrderByDescending(ToLambda<T>(propertyName));
}
private static Expression<Func<T, object>> ToLambda<T>(string propertyName)
{
var parameter = Expression.Parameter(typeof(T));
var property = Expression.Property(parameter, propertyName);
var propAsObject = Expression.Convert(property, typeof(object));
return Expression.Lambda<Func<T, object>>(propAsObject, parameter);
}
}
Regards, Mark.
I liked the answer from #Mark Powell, but as #ShuberFu said, it gives the error LINQ to Entities only supports casting EDM primitive or enumeration types.
Removing var propAsObject = Expression.Convert(property, typeof(object)); didn't work with properties that were value types, such as integer, as it wouldn't implicitly box the int to object.
Using Ideas from Kristofer Andersson and Marc Gravell I found a way to construct the Queryable function using the property name and have it still work with Entity Framework. I also included an optional IComparer parameter. Caution: The IComparer parameter does not work with Entity Framework and should be left out if using Linq to Sql.
The following works with Entity Framework and Linq to Sql:
query = query.OrderBy("ProductId");
And #Simon Scheurer this also works:
query = query.OrderBy("ProductCategory.CategoryId");
And if you are not using Entity Framework or Linq to Sql, this works:
query = query.OrderBy("ProductCategory", comparer);
Here is the code:
public static class IQueryableExtensions
{
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> query, string propertyName, IComparer<object> comparer = null)
{
return CallOrderedQueryable(query, "OrderBy", propertyName, comparer);
}
public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> query, string propertyName, IComparer<object> comparer = null)
{
return CallOrderedQueryable(query, "OrderByDescending", propertyName, comparer);
}
public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> query, string propertyName, IComparer<object> comparer = null)
{
return CallOrderedQueryable(query, "ThenBy", propertyName, comparer);
}
public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> query, string propertyName, IComparer<object> comparer = null)
{
return CallOrderedQueryable(query, "ThenByDescending", propertyName, comparer);
}
/// <summary>
/// Builds the Queryable functions using a TSource property name.
/// </summary>
public static IOrderedQueryable<T> CallOrderedQueryable<T>(this IQueryable<T> query, string methodName, string propertyName,
IComparer<object> comparer = null)
{
var param = Expression.Parameter(typeof(T), "x");
var body = propertyName.Split('.').Aggregate<string, Expression>(param, Expression.PropertyOrField);
return comparer != null
? (IOrderedQueryable<T>)query.Provider.CreateQuery(
Expression.Call(
typeof(Queryable),
methodName,
new[] { typeof(T), body.Type },
query.Expression,
Expression.Lambda(body, param),
Expression.Constant(comparer)
)
)
: (IOrderedQueryable<T>)query.Provider.CreateQuery(
Expression.Call(
typeof(Queryable),
methodName,
new[] { typeof(T), body.Type },
query.Expression,
Expression.Lambda(body, param)
)
);
}
}
Yes, I don't think there's another way than Reflection.
Example:
query = query.OrderBy(x => x.GetType().GetProperty("ProductId").GetValue(x, null));
query = query.OrderBy(x => x.GetType().GetProperty("ProductId").GetValue(x, null));
Trying to recall exact syntax off the top of my head but I think that is correct.
Warning ⚠️
You just can use Reflection in case that data is in-memory. Otherwise, you will see some error like below when you work with Linq-2-EF, Linq-2-SQL, etc.
#Florin Vîrdol's comment
LINQ to Entities does not recognize the method 'System.Object
GetValue(System.Object)' method and this method cannot be translated
into a store expression.
Why 🤔
Because when you write code to provide a query to Linq query provider. It is first translated into an SQL statement and then executed on the database server.
(See image below, from https://www.tutorialsteacher.com/linq/linq-expression)
Solution ✅
By using Expression tree, you can write a generic method like this
public static IEnumerable<T> OrderDynamic<T>(IEnumerable<T> Data, string propToOrder)
{
var param = Expression.Parameter(typeof(T));
var memberAccess = Expression.Property(param, propToOrder);
var convertedMemberAccess = Expression.Convert(memberAccess, typeof(object));
var orderPredicate = Expression.Lambda<Func<T, object>>(convertedMemberAccess, param);
return Data.AsQueryable().OrderBy(orderPredicate).ToArray();
}
And use it like this
var result = OrderDynamic<Student>(yourQuery, "StudentName"); // string property
or
var result = OrderDynamic<Student>(yourQuery, "Age"); // int property
And it's also working with in-memory by converting your data into IQueryable<TElement> in your generic method return statement like this
return Data.AsQueryable().OrderBy(orderPredicate).ToArray();
See the image below to know more in-depth.
Demo on dotnetfiddle
More productive than reflection extension to dynamic order items:
public static class DynamicExtentions
{
public static object GetPropertyDynamic<Tobj>(this Tobj self, string propertyName) where Tobj : class
{
var param = Expression.Parameter(typeof(Tobj), "value");
var getter = Expression.Property(param, propertyName);
var boxer = Expression.TypeAs(getter, typeof(object));
var getPropValue = Expression.Lambda<Func<Tobj, object>>(boxer, param).Compile();
return getPropValue(self);
}
}
Example:
var ordered = items.OrderBy(x => x.GetPropertyDynamic("ProductId"));
Also you may need to cache complied lambas(e.g. in Dictionary<>)
Reflection is the answer!
typeof(YourType).GetProperty("ProductId").GetValue(theInstance);
There's lots of things you can do to cache the reflected PropertyInfo, check for bad strings, write your query comparison function, etc., but at its heart, this is what you do.
Also Dynamic Expressions can solve this problem.
You can use string-based queries through LINQ expressions that could have been dynamically constructed at run-time.
var query = query
.Where("Category.CategoryName == #0 and Orders.Count >= #1", "Book", 10)
.OrderBy("ProductId")
.Select("new(ProductName as Name, Price)");
I think we can use a powerful tool name Expression an in this case use it as an extension method as follows:
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string ordering, bool descending)
{
var type = typeof(T);
var property = type.GetProperty(ordering);
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExp = Expression.Lambda(propertyAccess, parameter);
MethodCallExpression resultExp =
Expression.Call(typeof(Queryable), (descending ? "OrderByDescending" : "OrderBy"),
new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExp));
return (IOrderedQueryable<T>)source.Provider.CreateQuery<T>(resultExp);
}

Categories