我的项目中有一个扩展名,让我用一个字符串对IEnumerable
进行排序,这样就可以更加动态地进行排序。
所以,如果我有这些模型:
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;}
}
我可以分两种方式:
myList.OrderByField("RecordName ");
myList.OrderByField("MyChild.SavedDate");
但是,如果我的对象具有ICollection
属性,例如ICollection<ChildModel> MyChildren
,我可以像我这样硬编码:
myList
.OrderBy(m => m.MyChildren
.OrderByDescending(c => c.SavedDate).FirstOrDefault().SavedDate);
得到我想要的东西。
我的问题是,如何更新我的扩展方法以允许获得相同的结果:
myList.OrderByField("MyChildren.SavedDate");
这是我目前的扩展名:
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;
}
}
我在考虑检查prop
的类型并执行if... else
语句,但我不确定。
也许是这样的:
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);
}
}
答案 0 :(得分:0)
Max
函数是一种扩展方法,而不是IEnumerable
或ICollection
的成员方法。你必须从它的班级Enumerable
中调用它。
以下是如何通过表达式树调用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));