C#、LINQ 一种通用排序方法,用于按对象属性和嵌套属性对列表进行排序
C#, LINQ a generic sort method to sort a list by object properties and nested properties
我的 EF 模型中有一个名为 User 的实体:
public class User
{
public int UserId { get; set; }
public Branch HomeLocation{ get; set; }
public string CellPhone { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string UserCode { get; set; }
}
分支是模型中的另一个实体:
public class Branch
{
public int BranchId { get; set; }
public string BranchName{ get; set; }
public string Address { get; set; }
}
我的要求是获取用户列表并将其显示在网格上,然后按某些列(一次一个)对列表进行排序。例如,按用户名、名字、姓氏和家庭位置排序。按homelocation排序时,应该按分行名排序。
我有很多这样的网格也显示其他数据。所以我想开发一个通用的排序机制,我已经使用 Google 中的一些示例实现了它,例如 this one:
public class GenericSorter<T>
{
public IEnumerable<T> Sort(IEnumerable<T> source, string sortBy, string sortDirection)
{
var param = Expression.Parameter(typeof(T), "item");
var sortExpression = Expression.Lambda<Func<T, object>>
(Expression.Convert(Expression.Property(param, sortBy), typeof(object)), param);
switch (sortDirection.ToLower())
{
case "asc":
return source.AsQueryable<T>().OrderBy<T, object>(sortExpression);
default:
return source.AsQueryable<T>().OrderByDescending<T, object>(sortExpression);
}
}
}
然而,按家庭位置排序失败,因为它需要按用户实体的内部 属性 排序。我也尝试过使用 Dynamic LINQ library,但没有成功。
更新:请注意,我必须对列表进行排序,而不是 IQueryable,因为我的列表包含使用 AE 加密的字段,不支持数据库级排序。
有人能告诉我如何从内部 属性 实现动态排序吗?
更新 2:我按照示例并使用扩展方法实现了排序,这就是它在我的列表中的应用方式:
var users = (from u in context.Users.Include("Branch")
where (u.FkBranchId == branchId || branchId == -1) && u.IsActive
&& (searchTerm == string.Empty || (u.FirstName.Contains(searchTerm) || u.LastName.Equals(searchTerm)
|| u.UserName.Contains(searchTerm) || u.UserCode.Contains(searchTerm)))
select u).ToList();
var rowCount = users.Count;
var orderedList = users.OrderBy(sortInfo.SortColumn).Skip(pageInfo.Skip).Take(pageInfo.PageSize).ToList();
但是我收到以下错误:
'System.Linq.Expressions.Expression1[System.Func
2[ClientData.User,System.String]]' 类型的对象无法转换为 'System.Func`2[ClientData.User,System.String]'.
类型
错误从以下抛出:
object result = typeof(Enumerable).GetMethods().Single(
method => method.Name == methodName
&& method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 2
&& method.GetParameters().Length == 2)
.MakeGenericMethod(typeof(T), type)
.Invoke(null, new object[] { source, lambda });
在此之后,我在某些情况下收到以下错误,如评论中所述:
改编来自@MarcGravell 的代码here。
public static class EnumerableExtensions
{
public static IOrderedEnumerable<T> OrderBy<T>(
this IEnumerable<T> source,
string property)
{
return ApplyOrder<T>(source, property, "OrderBy");
}
public static IOrderedEnumerable<T> OrderByDescending<T>(
this IEnumerable<T> source,
string property)
{
return ApplyOrder<T>(source, property, "OrderByDescending");
}
public static IOrderedEnumerable<T> ThenBy<T>(
this IOrderedEnumerable<T> source,
string property)
{
return ApplyOrder<T>(source, property, "ThenBy");
}
public static IOrderedEnumerable<T> ThenByDescending<T>(
this IOrderedEnumerable<T> source,
string property)
{
return ApplyOrder<T>(source, property, "ThenByDescending");
}
static IOrderedEnumerable<T> ApplyOrder<T>(
IEnumerable<T> source,
string property,
string methodName)
{
string[] props = property.Split('.');
Type type = typeof(T);
ParameterExpression arg = Expression.Parameter(type, "x");
Expression expr = arg;
foreach (string prop in props)
{
// use reflection (not ComponentModel) to mirror LINQ
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
object result = typeof(Enumerable).GetMethods().Single(
method => method.Name == methodName
&& method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 2
&& method.GetParameters().Length == 2)
.MakeGenericMethod(typeof(T), type)
.Invoke(null, new object[] { source, lambda.Compile() });
return (IOrderedEnumerable<T>)result;
}
}
已更新
从列表<>中使用它:
var list = new List<MyModel>();
list = list.OrderBy("MyProperty");
我的 EF 模型中有一个名为 User 的实体:
public class User
{
public int UserId { get; set; }
public Branch HomeLocation{ get; set; }
public string CellPhone { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string UserCode { get; set; }
}
分支是模型中的另一个实体:
public class Branch
{
public int BranchId { get; set; }
public string BranchName{ get; set; }
public string Address { get; set; }
}
我的要求是获取用户列表并将其显示在网格上,然后按某些列(一次一个)对列表进行排序。例如,按用户名、名字、姓氏和家庭位置排序。按homelocation排序时,应该按分行名排序。
我有很多这样的网格也显示其他数据。所以我想开发一个通用的排序机制,我已经使用 Google 中的一些示例实现了它,例如 this one:
public class GenericSorter<T>
{
public IEnumerable<T> Sort(IEnumerable<T> source, string sortBy, string sortDirection)
{
var param = Expression.Parameter(typeof(T), "item");
var sortExpression = Expression.Lambda<Func<T, object>>
(Expression.Convert(Expression.Property(param, sortBy), typeof(object)), param);
switch (sortDirection.ToLower())
{
case "asc":
return source.AsQueryable<T>().OrderBy<T, object>(sortExpression);
default:
return source.AsQueryable<T>().OrderByDescending<T, object>(sortExpression);
}
}
}
然而,按家庭位置排序失败,因为它需要按用户实体的内部 属性 排序。我也尝试过使用 Dynamic LINQ library,但没有成功。
更新:请注意,我必须对列表进行排序,而不是 IQueryable,因为我的列表包含使用 AE 加密的字段,不支持数据库级排序。
有人能告诉我如何从内部 属性 实现动态排序吗?
更新 2:我按照示例并使用扩展方法实现了排序,这就是它在我的列表中的应用方式:
var users = (from u in context.Users.Include("Branch")
where (u.FkBranchId == branchId || branchId == -1) && u.IsActive
&& (searchTerm == string.Empty || (u.FirstName.Contains(searchTerm) || u.LastName.Equals(searchTerm)
|| u.UserName.Contains(searchTerm) || u.UserCode.Contains(searchTerm)))
select u).ToList();
var rowCount = users.Count;
var orderedList = users.OrderBy(sortInfo.SortColumn).Skip(pageInfo.Skip).Take(pageInfo.PageSize).ToList();
但是我收到以下错误:
'System.Linq.Expressions.Expression1[System.Func
2[ClientData.User,System.String]]' 类型的对象无法转换为 'System.Func`2[ClientData.User,System.String]'.
错误从以下抛出:
object result = typeof(Enumerable).GetMethods().Single(
method => method.Name == methodName
&& method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 2
&& method.GetParameters().Length == 2)
.MakeGenericMethod(typeof(T), type)
.Invoke(null, new object[] { source, lambda });
在此之后,我在某些情况下收到以下错误,如评论中所述:
改编来自@MarcGravell 的代码here。
public static class EnumerableExtensions
{
public static IOrderedEnumerable<T> OrderBy<T>(
this IEnumerable<T> source,
string property)
{
return ApplyOrder<T>(source, property, "OrderBy");
}
public static IOrderedEnumerable<T> OrderByDescending<T>(
this IEnumerable<T> source,
string property)
{
return ApplyOrder<T>(source, property, "OrderByDescending");
}
public static IOrderedEnumerable<T> ThenBy<T>(
this IOrderedEnumerable<T> source,
string property)
{
return ApplyOrder<T>(source, property, "ThenBy");
}
public static IOrderedEnumerable<T> ThenByDescending<T>(
this IOrderedEnumerable<T> source,
string property)
{
return ApplyOrder<T>(source, property, "ThenByDescending");
}
static IOrderedEnumerable<T> ApplyOrder<T>(
IEnumerable<T> source,
string property,
string methodName)
{
string[] props = property.Split('.');
Type type = typeof(T);
ParameterExpression arg = Expression.Parameter(type, "x");
Expression expr = arg;
foreach (string prop in props)
{
// use reflection (not ComponentModel) to mirror LINQ
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
object result = typeof(Enumerable).GetMethods().Single(
method => method.Name == methodName
&& method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 2
&& method.GetParameters().Length == 2)
.MakeGenericMethod(typeof(T), type)
.Invoke(null, new object[] { source, lambda.Compile() });
return (IOrderedEnumerable<T>)result;
}
}
已更新
从列表<>中使用它:
var list = new List<MyModel>();
list = list.OrderBy("MyProperty");