[英]Dynamically build an Expression tree for sorting
I have an extension in my project that let's me sort an IEnumerable
with a string, so that sorting can be done more dynamically. 我的项目中有一个扩展名,可以让我对带有字符串的IEnumerable
进行排序,以便可以更动态地进行排序。
So, if I have these models: 因此,如果我有以下模型:
public MyModel
{
public int Id {get; set;}
public string RecordName {get; set;}
public ChildModel MyChild {get; set;}
}
public ChildModel
{
public int ChildModelId {get; set;}
public DateTime SavedDate {get; set;}
}
I can sort two ways: 我可以通过两种方式进行排序:
myList.OrderByField("RecordName ");
myList.OrderByField("MyChild.SavedDate");
However, if my object has an ICollection
property, like ICollection<ChildModel> MyChildren
I can hard code my sort like this: 但是,如果我的对象具有ICollection
属性,例如ICollection<ChildModel> MyChildren
,则可以像这样对我的排序进行硬编码:
myList
.OrderBy(m => m.MyChildren
.OrderByDescending(c => c.SavedDate).FirstOrDefault().SavedDate);
And get what I want. 并得到我想要的。
My question is , how can I update my extension method to allow to get the same results with this: 我的问题是 ,我该如何更新扩展方法以允许获得与此相同的结果:
myList.OrderByField("MyChildren.SavedDate");
Here is my current extension: 这是我当前的扩展名:
public static class MkpExtensions
{
public static IEnumerable<T> OrderByField<T>(this IEnumerable<T> list, string sortExpression)
{
sortExpression += "";
string[] parts = sortExpression.Split(' ');
bool descending = false;
string fullProperty = "";
if (parts.Length > 0 && parts[0] != "")
{
fullProperty = parts[0];
if (parts.Length > 1)
{
descending = parts[1].ToLower().Contains("esc");
}
ParameterExpression inputParameter = Expression.Parameter(typeof(T), "p");
Expression propertyGetter = inputParameter;
foreach (string propertyPart in fullProperty.Split('.'))
{
PropertyInfo prop = propertyGetter.Type.GetProperty(propertyPart);
if (prop == null)
throw new Exception("No property '" + fullProperty + "' in + " + propertyGetter.Type.Name + "'");
propertyGetter = Expression.Property(propertyGetter, prop);
}
Expression conversion = Expression.Convert(propertyGetter, typeof(object));
var getter = Expression.Lambda<Func<T, object>>(conversion, inputParameter).Compile();
if (descending)
return list.OrderByDescending(getter);
else
return list.OrderBy(getter);
}
return list;
}
}
I was thinking of checking the type of the prop
and doing an if... else
statement, but I'm not sure. 我当时正在考虑检查prop
的类型并执行if... else
语句,但是我不确定。
Maybe something like this: 也许是这样的:
foreach (string propertyPart in fullProperty.Split('.'))
{
var checkIfCollection = propertyGetter.Type.GetInterfaces()//(typeof (ICollection<>).FullName);
.Any(x => x.IsGenericType &&
(x.GetGenericTypeDefinition() == typeof(ICollection<>) || x.GetGenericTypeDefinition() == typeof(IEnumerable<>)));
if (checkIfCollection)
{
// Can I get this to do something like
// myList.OrderBy(m => m.MyChildren.Max(c => c.SavedDate));
// So far, I can get the propertyGetter type, and the type of the elements:
var pgType = propertyGetter.Type;
var childType = pgType.GetGenericArguments().Single();
// Now I want to build the expression tree to get the max
Expression left =
Expression.Call(propertyGetter, pgType.GetMethod("Max", System.Type.EmptyTypes));
// But pgType.GetMethod isn't working
}
else
{
PropertyInfo prop = propertyGetter.Type.GetProperty(propertyPart);
if (prop == null)
throw new Exception("No property '" + fullProperty + "' in + " + propertyGetter.Type.Name + "'");
propertyGetter = Expression.Property(propertyGetter, prop);
}
}
The Max
function is an extension method, not a member method of IEnumerable
or ICollection
. Max
函数是扩展方法,而不是IEnumerable
或ICollection
的成员方法。 You must call it from it's class Enumerable
. 您必须从其Enumerable
类中调用它。
Here is an example of how to call Max
through expression tree: 这是如何通过表达式树调用Max
的示例:
IEnumerable<int> list = new List<int> { 3, 5, 7, 2, 12, 1 };
var type = typeof(Enumerable); //This is the static class that contains Max
//Find The overload of Max that matches the list
var maxMethod = type.GetMethod("Max", new Type[] { typeof(IEnumerable<int>) });
ParameterExpression p = Expression.Parameter(typeof(IEnumerable<int>));
//Max is static, so the calling object is null
var exp = Expression.Call(null, maxMethod, p);
var lambda = Expression.Lambda<Func<IEnumerable<int>, int>>(exp, p);
Console.WriteLine(lambda.Compile()(list));
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.