使用字符串访问列表中的子属性
本文关键字:属性 列表 字符串 访问 | 更新日期: 2023-09-27 18:29:14
我正在编写一个扩展方法,该方法允许我使用字符串而不是Lambda表达式对IEnumerable列表对象执行OrderBy。它适用于简单的属性。然而,我正在尝试找出如何允许嵌套属性。
如果我的模型看起来像这样:
public class Submission
{
public int SubmissionId {get; set;}
public string Description {get; set;}
public int ProjectId {get; set;}
// Parent object
public Project ParentProject {get; set;}
}
public class Project
{
public int ProjectId {get; set;}
public string FullTitle {get; set;}
}
我可以通过使用这个来完成订单:
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);
}
// This line was needed
Expression conversion = Expression.Convert(propertyGetter, typeof(object));
var getter = Expression.Lambda<Func<T, object>>(propertyGetter, inputParameter).Compile();
if (descending)
return list.OrderByDescending(x => prop.GetValue(x, null));
else
return list.OrderBy(x => prop.GetValue(x, null));
}
return list;
}
}
我的代码是这样的:
public List<Submission> SortedSubmissions (bool simple = true) {
var project1 = new Project { ProjectId = 1, FullTitle = "Our Project"};
var project2 = new Project { ProjectId = 2, FullTitle = "A Project"};
List<Submission> listToSort = new List<Submission>
{
new Submission { SubmissionId = 1, Description = "First Submission",
ProjectId = project1.ProjectId, ParentProject = project1 } ,
new Submission { SubmissionId = 2, Description = "Second Submission",
ProjectId = project1.ProjectId, ParentProject = project1 } ,
new Submission { SubmissionId = 3, Description = "New Submission",
ProjectId = project2.ProjectId, ParentProject = project2 }
};
var simpleField = "Description";
// This would have the submissions sorted (1, 3, 2)
var simpleSort = listToSort.OrderByField(simpleField + " asc").ToList();
// Need to see if I can get this to work
var nestedField = "Project.FullTitle";
// This would have the submissions sorted (3, 1, 2)
return listToSort.OrderByField(nestedField + " asc").ToList();
}
我希望我能把自己解释清楚。这能做到吗?
更新:我使用了AndréKops代码并在上面进行了调整,但得到了这个错误:System.Nullable'1[System.Int32]' cannot be used for return type 'System.Object'
这对代码来说是一个相当大的变化,但表达式树非常适合:
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;
}
}
此示例还允许嵌套深度超过2个属性。
而且对于大列表来说可能更快。
怎么样?
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");
}
string fieldName;
PropertyInfo parentProp = null;
PropertyInfo prop = null;
if (fullProperty.Contains("."))
{
// A nested property
var parentProperty = fullProperty.Remove(fullProperty.IndexOf("."));
fieldName = fullProperty.Substring(fullProperty.IndexOf("."));
parentProp = typeof(T).GetProperty(parentProperty);
prop = parentProp.PropertyType.GetProperty(fieldName);
}
else
{
// A simple property
prop = typeof(T).GetProperty(fullProperty);
}
if (prop == null)
{
throw new Exception("No property '" + fullProperty + "' in + " + typeof(T).Name + "'");
}
if (parentProp != null)
{
if (descending)
return list.OrderByDescending(x => prop.GetValue(parentProp.GetValue(x, null), null));
else
return list.OrderBy(x => prop.GetValue(parentProp.GetValue(x, null), null));
}
else
{
if (descending)
return list.OrderByDescending(x => prop.GetValue(x, null));
else
return list.OrderBy(x => prop.GetValue(x, null));
}
}
return list;
}