首页 > 解决方案 > 与属性名称匹配的按字符串排序给出空引用

问题描述

我对以下排序方案有疑问。

var model = DbContext
           .Select(s => new ViewModel)
           .OrderBy(paging.Sorting);

paging.Sorting 是来自客户端的字符串,从不为 NULL。

如果列具有空值,则排序将引发空引用异常。

有没有办法使用 lamdba 表达式?

这是我经常遇到的错误,我正在尝试找到一个可靠的解决方案。

例子:

var people = DbContext.People
                      .Select(p => new PersonViewModel{
                      Name = p.Name,
                      ManagerId = p.ManagerId,
                      DepartmentId = p.DepartmentId
                      })
                      .OrderBy(paging.Sorting)

有时 paging.Sorting = "DepartmentId DESC"

或者

paging.Sorting = "ManagerId ASC"

有些人没有经理,并且 Id 的值为空。所以排序会抛出一个 null ref 异常。

标签: c#asp.net-mvclinq

解决方案


按属性名称排序,没有类型反射

public static class IQueryableExtensions
    {
      public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string 
      propertyName)
      {
      return (IQueryable<T>)OrderBy((IQueryable)source, propertyName);
      }

      public static IQueryable OrderBy(this IQueryable source, string propertyName)
      {
        var x = Expression.Parameter(source.ElementType, "x");
        var body = propertyName.Split('.').Aggregate<string, Expression>(x, 
        Expression.PropertyOrField);

        var selector = Expression.Lambda
         (Expression.PropertyOrField(x, propertyName), x);

         return source.Provider.CreateQuery(
            Expression.Call(typeof(Queryable), "OrderBy", new Type[] { 
             source.ElementType, selector.Body.Type },
                 source.Expression, selector
                 ));
      }

      public static IQueryable<T> OrderByDescending<T>(this IQueryable<T> source, 
      string propertyName)
      {
        return (IQueryable<T>)OrderByDescending((IQueryable)source, propertyName);
      }

      public static IQueryable OrderByDescending(this IQueryable source, string 
      propertyName)
      {
        var x = Expression.Parameter(source.ElementType, "x");
        var selector = Expression.Lambda(Expression.PropertyOrField(x, 
        propertyName),x);
        return source.Provider.CreateQuery(
            Expression.Call(typeof(Queryable), "OrderByDescending", new Type[] { 
             source.ElementType, selector.Body.Type },
                 source.Expression, selector
                 ));
      }
}

那么你可以像这样使用它

var people = DbContext.People
                      .Select(p => new PersonViewModel{
                      Name = p.Name,
                      ManagerId = p.ManagerId,
                      DepartmentId = p.DepartmentId
                      })
                      .OrderBy(paging.Sorting)

或者你可以使用类型反射(没有 IQueryableExtensions)来做到这一点(虽然它不是很有效):像这样

var people = DbContext.People
                          .Select(p => new PersonViewModel{
                          Name = p.Name,
                          ManagerId = p.ManagerId,
                          DepartmentId = p.DepartmentId
                          })
                          .OrderBy(o => o.GetType()
                          .GetProperty(paging.Sorting)
                          .GetValue(o, null))

推荐阅读