2013-01-12 45 views
7

Tôi đang cố gắng xây dựng một biểu thức để sắp xếp và tôi đã viết mã để sắp xếp danh sách của tôi bằng một thuộc tính.xây dựng một biểu thức với nhiều phân loại

Nhưng tôi cần phải sắp xếp nó trước hết bởi một thuộc tính, thứ hai bởi một thuộc tính khác và như vậy.

Tôi có nghĩa là tôi muốn xây dựng một biểu thức sẽ thực hiện một cái gì đó như thế: students.OrderBy(fistExpression.Compile()).ThenBy(secondImpression.Complie()).ThenBy(thirdExpression.Compile()).

Vậy làm cách nào để tự động đặt các phương thức đó ThenBy?

Đây là mã của tôi:

Type studentType = typeof(Student); 
ParameterExpression studentParam = Expression.Parameter(studentType, "x"); 
MemberInfo ageProperty = studentType.GetProperty("Age"); 
MemberExpression valueInNameProperty = 
    Expression.MakeMemberAccess(studentParam, ageProperty); 
Expression<Func<Student, int>> orderByExpression = 
    Expression<Func<Student, int>>.Lambda<Func<Student, int>>(valueInNameProperty, studentParam); 
var sortedStudents = students.OrderBy(orderByExpression.Compile()); 
+0

Để làm rõ - bạn đang tìm kiếm một cách để xây dựng một Biểu thức duy nhất, bạn có thể chuyển đến .OrderBy sẽ tái tạo chức năng truyền một chuỗi các Biểu thức tới .OrderBy và .ThenBy, co rrect? –

+0

Dupe http://stackoverflow.com/questions/41244/dynamic-linq-orderby – aquinas

Trả lời

2

Giải pháp của tôi:

public static Func<Student, object> BuildPredicate(string propertyName) 
{ 
    Type studentType = typeof(Student); 
    ParameterExpression studentParam = Expression.Parameter(studentType, "x"); 
    MemberInfo ageProperty = studentType.GetProperty(propertyName); 
    MemberExpression valueInNameProperty = Expression.MakeMemberAccess(studentParam, ageProperty); 
    UnaryExpression expression = Expression.Convert(valueInNameProperty, typeof (object)); 
    Expression<Func<Student, object>> orderByExpression = Expression.Lambda<Func<Student, object>>(expression, studentParam); 
    return orderByExpression.Compile(); 
} 

trong làm cho mã biểu hiện của bạn được thêm đúc để object.

Đó là cách bạn có thể tạo ra một chuỗi các ThenBy:

var sortedStudents = students.OrderBy(BuildPredicate("Age")); 
foreach (var property in typeof(Student).GetProperties().Where(x => !String.Equals(x.Name, "Age"))) 
{ 
    sortedStudents = sortedStudents.ThenBy(BuildPredicate(property.Name)); 
} 

var result = sortedStudents.ToList(); 

Cuối cùng, lớp Student mẫu:

public class Student 
{ 
    public int Age { get; set; } 
    public string Name { get; set; } 
} 

Cập nhật:

cách tiếp cận khác là sử dụng các thuộc tính để đánh dấu các mục thích hợp từ số Student của bạn để sử dụng chúng trong OrderByThenBy. Giống như:

public class Student 
{ 
    [UseInOrderBy] 
    public int Age { get; set; } 

    [UseInOrderBy(Order = 1)] 
    public string Name { get; set; } 
} 

[AttributeUsage(AttributeTargets.Property)] 
internal class UseInOrderByAttribute : Attribute 
{ 
    public int Order { get; set; } 
} 

Đó là cách bạn có thể xây dựng sắp xếp chuỗi sử dụng UseInOrderByAttribute:

Type studentType = typeof (Student); 
var properties = studentType.GetProperties() 
          .Select(x => new { Property = x, OrderAttribute = x.GetCustomAttribute<UseInOrderByAttribute>() }) 
          .Where(x => x.OrderAttribute != null) 
          .OrderBy(x => x.OrderAttribute.Order); 

var orderByProperty = properties.FirstOrDefault(x => x.OrderAttribute.Order == 0); 
if (orderByProperty == null) 
    throw new Exception(""); 

var sortedStudents = students.OrderBy(BuildPredicate(orderByProperty.Property.Name)); 
foreach (var property in properties.Where(x => x.Property.Name != orderByProperty.Property.Name)) 
{ 
    sortedStudents = sortedStudents.ThenBy(BuildPredicate(property.Property.Name)); 
} 

var result = sortedStudents.ToList(); 

Fix: BuildPredicate có thể bằng văn bản mà không cần dynamic. BuildPredicate mã mẫu được thay đổi.

1

Tôi giả định rằng bạn có các thuộc tính riêng tư mà bạn muốn có thể sắp xếp. Nếu bạn ví dụ có lớp này:

public class Student 
{ 
    public Student (int age, string name) 
    { 
     Age = age; 
     Name = name; 
    } 

    private string Name { get; set; } 

    public int Age { get; set; } 

    public override string ToString() 
    { 
     return string.Format ("[Student: Age={0}, Name={1}]", Age, Name); 
    } 
} 

Bạn có thể sử dụng phương pháp sau đây để xây dựng biểu thức mà sẽ có được cả hai tài sản công cộng và tư nhân:

public static Func<TType, TResult> CreateExpression<TType, TResult>(string propertyName) 
{ 
    Type type = typeof(TType); 
    ParameterExpression parameterExpression = Expression.Parameter(type, propertyName); 
    MemberInfo property = type.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); 
    MemberExpression valueInProperty = Expression.MakeMemberAccess(parameterExpression, property); 

    return Expression.Lambda<Func<TType,TResult>>(valueInProperty, parameterExpression).Compile(); 
} 

Ví dụ về sử dụng:

 var students = new [] { 
      new Student(20, "Ben"), 
      new Student(20, "Ceasar"), 
      new Student(20, "Adam"), 
      new Student(21, "Adam"), 
     }; 

     var sortedStudents = students 
      .OrderBy(CreateExpression<Student, string>("Name")) 
      .ThenBy(CreateExpression<Student, int>("Age")); 

     sortedStudents.ToList().ForEach(student => Console.WriteLine(student)); 
     /* 
     Prints: 
     [Student: Age=20, Name=Adam] 
     [Student: Age=21, Name=Adam] 
     [Student: Age=20, Name=Ben] 
     [Student: Age=20, Name=Ceasar] 
     */ 
Các vấn đề liên quan