【发布时间】:2016-05-09 20:35:28
【问题描述】:
我的项目中有一个扩展,可以让我用字符串对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);
}
}
【问题讨论】:
-
仅供参考,您可以像这样简化硬编码的嵌套排序:
myList.OrderBy(m => m.MyChildren.Max(c => c.SavedDate));。说到这里,当你的程序看到"MyChildren.SavedDate"时,它怎么知道按MyChildren升序排序,但按SavedDate降序排序? -
Max 将适用于子记录。在我的字符串中,我实际上使用
MyChildren.SavedDate asc或MyChildren.SavedDate desc并相应地翻转。.SavedDate假设是最近的……暂时。 ;)
标签: c# linq sorting expression-trees