Reputation: 421
everyone. I've tried in the past couple of hours to find an answer with Google and desperate tests using Immediate Window, but I think I'm not making the correct questions.
I have an OrderByExtender in my application, which I use to do sorts just using the property name and a boolean to tell if it's ASC or DESC sort.
public static IOrderedEnumerable<T> OrderBy<T>(this IEnumerable<T> collection, string key, bool isAscending)
{
LambdaExpression sortLambda = BuildLambda<T>(key);
if (isAscending)
return collection.OrderBy((Func<T, object>)sortLambda.Compile());
else
return collection.OrderByDescending((Func<T, object>)sortLambda.Compile());
}
private static LambdaExpression BuildLambda<T>(string key)
{
ParameterExpression TParameterExpression = Expression.Parameter(typeof(T), "p");
LambdaExpression sortLambda = Expression.Lambda(Expression.Convert(Expression.Property(TParameterExpression, key), typeof(object)), TParameterExpression);
return sortLambda;
}
It works like a charm when I use common-type properties (string, int, etc). But now I came up with the following scenario: I have an object called BusinessOrder, and inside it there is a property with the type Quarter. This Quarter object has three properties: Year(int), Quarter(int) and Abbreviation(string). I must do the OrderBy using the Abbreviation property. In other words, I must do:
BusinessOrderList.OrderBy(b => b.Quarter.Abbreviation);
But I want to put this kind of sort possibility inside my Extender, by passing as key parameter something like "Quarter.Abbreviation" and the Extender method understands that it's a matter of getting the property inside a "complex object".
I believe there's something I could do inside the "Expression.Lambda" method that creates the sortLambda variable, but I couldn't figure out how to replicate this behavior using Expressions. Can someone help me? Thanks in advance.
Upvotes: 1
Views: 802
Reputation: 15015
You can pass a Func
for sorting your collection
public static IOrderedEnumerable<T> OrderBy<T>(this IEnumerable<T> collection,
Func<IEnumerable<T>,IOrderedEnumerable<T>> OrderBy)
{
if(OrderBy != null)
return OrderBy(collection);
return collection;
}
You have to call it like this
{
Func<IEnumerable<Book>,IOrderedEnumerable<Book>> sort =
list => list.OrderBy(B=>B.Author.Name).ThenByDescending(B=>B.Title) ;
List.OrderBy(sort);
}
Upvotes: 0
Reputation: 1837
If possible, I would recommend replacing the string key
parameter with a Expression<Func<T, object>> key
parameter. That way, your function call can be BusinessOrderList.OrderBy(b => b.Quarter.Abbreviation);
just as you described. Since all the OrderBy/OrderByDescending methods accept this expression as an alternative to a string anyway, I don't see what benefit you derive from keeping key
as a string.
Upvotes: 0
Reputation: 545
You cannot access complex property in one step. You must build expressions chain recursively:
private Func<TInput, object> BuildLambda<TInput>(string complexPropertyPath)
{
var parameter = Expression.Parameter(typeof(TInput), "p");
var propertyPathParts = complexPropertyPath.Split('.');
MemberExpression complexPropertyAccessExpression = null;
foreach (var propertyPathPart in propertyPathParts)
{
complexPropertyAccessExpression = complexPropertyAccessExpression == null
? Expression.Property(parameter, propertyPathPart)
: Expression.Property(complexPropertyAccessExpression, propertyPathPart);
}
var lambda = (Func<TInput, object>)Expression.Lambda(complexPropertyAccessExpression, parameter).Compile();
return lambda;
}
Upvotes: 2