Entity framework 按表达式排序

Entity framework order by expression

我在启用远程过滤、排序和分组的 ExtJS 网格中遇到了问题。

System.NotSupportedException: Unable to cast the type 'System.Nullable`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types.
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.ValidateAndAdjustCastTypes(TypeUsage toType, TypeUsage fromType, Type toClrType, Type fromClrType)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.GetCastTargetType(TypeUsage fromType, Type toClrType, Type fromClrType, Boolean preserveCastForDateTime)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.CreateCastExpression(DbExpression source, Type toClrType, Type fromClrType)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.ConvertTranslator.TranslateUnary(ExpressionConverter parent, UnaryExpression unary, DbExpression operand)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.UnaryTranslator.TypedTranslate(ExpressionConverter parent, UnaryExpression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
bij  System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input, DbExpressionBinding& binding)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, DbExpression& source, DbExpressionBinding& sourceBinding, DbExpression& lambda)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.UnarySequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.UnarySequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, DbExpression& source, DbExpressionBinding& sourceBinding, DbExpression& lambda)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SelectTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
bij Systm.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.Convert()
bij System.Data.Entity.Core.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable`1 forMergeOption)
bij System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClassc.<GetResultsAsync>b__a()
bij System.Data.Entity.Core.Objects.ObjectContext.<ExecuteInTransactionAsync>d__3d`1.MoveNext()

下面的代码示例转换输入模型(指示方向的字符串属性和 属性 名称)并动态生成 LINQ 脚本,它将用于存储库模式,因此也 Entity Framework.此脚本适用于不可为 null 的类型,但在对可为 null 的属性进行排序时会出现上述错误。

Expression<Func<Task, object>> orderByClause = default(Expression<Func<Task, object>>);
if (sortObjects != null)
{
       foreach (Order sortObject in sortObjects)
       {
        // Get type and property of type
        ParameterExpression parameter = Expression.Parameter(typeof(Task), "x");                       
        PropertyInfo property = typeof(Task).GetProperty(sortObject.Property);

         // Create left hand side of the lambda: x => x.PROPERTY
        MemberExpression propertyAccess = Expression.Property(parameter, property);
        LambdaExpression orderByExp = Expression.Lambda(propertyAccess, parameter);

        // Create expression from lambda
        MemberExpression orderByExpression = Expression.Property(parameter, sortObject.Property);
        Expression conversion = Expression.Convert(orderByExpression, typeof(object));

        Expression<Func<Task,object>> lambda = Expression.Lambda<Func<Task, object>>(conversion, parameter);
        if (orderByClause == default(Expression<Func<Task, object>>))
       {
         orderByClause = lambda;
       }
       else
       {
          InvocationExpression invokedExpr = Expression.Invoke(lambda, orderByClause.Parameters.Cast<Expression>());
           orderByClause = Expression.Lambda<Func<Task, object>>(Expression.AndAlso(orderByClause.Body, invokedExpr), orderByClause.Parameters);
       }
      }
 }

  return orderByClause;

这里的问题可能是对象和可空类型之间的转换。我需要能够在表达式中包含可为 null 的类型。

我在考虑两个选项:

1) 使用某种泛型方法来定义 属性 类型而不是使用对象类型

2) 将可为 null 的 属性 框起来,以便它可以转换为对象。

我认为选项 2 最简单,但我有点卡在这里了。

更新:

我仍然没有找到解决方案,但在解决此问题之前我有一个解决方法。现在排序键不再是一个对象,而是一个泛型类型。这种情况将始终有效,因为不再发生任何装箱:

 protected virtual Expression<Func<T, TKey>> GetSorting<TKey>(string ordering)
    {
        IEnumerable<Order> sortObjects = string.IsNullOrEmpty(ordering) ? null : JsonConvert.DeserializeObject<IEnumerable<Order>>(ordering);

        Expression<Func<T, TKey>> orderByClause = default(Expression<Func<T, TKey>>);
        if (sortObjects != null)
        {
            foreach (Order sortObject in sortObjects)
            {
                // Get type and property of type
                ParameterExpression parameter = Expression.Parameter(typeof(T), "x");
                PropertyInfo property = typeof(T).GetProperty(sortObject.Property);

                // Create left hand side of the lambda: x => x.PROPERTY
                MemberExpression propertyAccess = !sortObject.ComplexType ? Expression.Property(parameter, property) : Expression.PropertyOrField(Expression.Property(parameter, property), sortObject.ComplexTypeProperty);
                MemberExpression orderByExpression = !sortObject.ComplexType ? Expression.Property(parameter, sortObject.Property) : Expression.PropertyOrField(Expression.Property(parameter, property), sortObject.ComplexTypeProperty);

                // Create expression from lambda
                Expression<Func<T, TKey>> lambda = Expression.Lambda<Func<T, TKey>>(orderByExpression, parameter);
                if (orderByClause == default(Expression<Func<T, TKey>>))
                {
                    orderByClause = lambda;
                }
                else
                {
                    InvocationExpression invokedExpr = Expression.Invoke(lambda, orderByClause.Parameters.Cast<Expression>());
                    orderByClause = Expression.Lambda<Func<T, TKey>>(Expression.AndAlso(orderByClause.Body, invokedExpr), orderByClause.Parameters);
                }
            }
        }

        return orderByClause;
    }

我是这样调用这个方法的。根据用户的输入,我确定要对哪个 属性 进行排序。使用反射,我检索其类型,然后将其作为排序方法的类型传递。

switch (propertyType)
  {
          case "Int32":
            Expression<Func<Resource, int?>> orderIntExpression = this.GetSorting<int?>(ordering);
            return await this.GetResources<int?>(page, pageSize, orderIntExpression, base.IsAscending(ordering), selector, mergedQuery);
           default:
              Expression<Func<Resource, object>> orderDefaultExpression = this.GetSorting<object>(ordering);
              return await this.GetResources<object>(page, pageSize, orderDefaultExpression, base.IsAscending(ordering), selector, mergedQuery);
  }

这可行,但我认为它的可扩展性不是很好。我在这里没有看到任何简单或漂亮的修复程序来动态地将类型传递给排序方法。对此事有什么建议吗?

经过大量的反复试验,我再次通过编写下面的代码拯救了自己。它尚未完成,但它允许对可空类型进行排序和分组。

 Expression<Func<T, dynamic>> sortExpression = default(Expression<Func<T, dynamic>>);
 IEnumerable<Order> sortObjects = string.IsNullOrEmpty(ordering) ? null : JsonConvert.DeserializeObject<IEnumerable<Order>>(ordering);
 if (sortObjects != null)
 {
   foreach (Order sortObject in sortObjects)
   {
      Type type = typeof(T);
      ParameterExpression parameter = Expression.Parameter(type, "x");
      MemberExpression propertyReference = Expression.Property(parameter, sortObject.Property);
      Expression conversion = Expression.Convert(propertyReference, typeof(object));
      sortExpression = Expression.Lambda<Func<T, dynamic>>(conversion, new[] { parameter });

                break;
   }
}
return sortExpression;