2016-01-06 124 views
0

我正在写一个扩展方法,它允许我使用字符串而不是Lambda表达式对IEnumerable列表对象执行OrderBy。它适用于简单的属性。但是,我想弄清楚如何允许嵌套属性。使用字符串访问列表中的子属性

如果我的模式是这样的:

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(); 
} 

我希望我

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;} 
} 

我可以用这个做一个排序依据清楚地解释自己。这可以做到吗?

更新:我用安德烈芭斯代码及以上的调整,但出现此错误:System.Nullable'1[System.Int32]' cannot be used for return type 'System.Object'

+0

我认为这是有帮助的,如果你可以举一个你想发送什么样的字符串的例子,结果如何。 –

+0

最后一段代码显示了我将如何发送字符串“Project.FullTitle”,我希望列表的结果由父属性排序。 –

+0

如果我正确理解你,你试图通过他们的名字访问字符串中的字段?如果是这样,我不认为有一种方法可以在C#中反映出来。相反,我会设置一些条件(如果你知道最初的字段是什么样的)。 –

回答

1

它在你的代码中的相当大的变化,但表达式树是完美的:

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个属性。

对于大型列表可能会更快。

+0

这看起来不错,但是...在我的实际代码中,我有一些可以为空的整数的字段。我得到这个:'附加信息:类型'System.Nullable'1 [System.Int32]'的表达式不能用于返回类型'System.Object'' –

+0

找到该问题的一个修复程序,并更新了您的文章。非常感谢! –

+0

奇怪的是,这种错误只发生在可为空的整数。你怎么修好它的?我没有看到任何更新? –

0

这个怎么样?

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; 
    } 
相关问题