实体框架按表达式排序

时间:2015-09-29 11:23:18

标签: c# entity-framework lambda linq-to-entities 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脚本,该脚本将用于存储库模式,因此也将用于实体框架。此脚本适用于非可空类型,但在对可空属性进行排序时会出现上述错误。

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;

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

我在考虑两种选择:

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

2)装入可以为空的属性,以便它可以被转换为一个对象。

我认为选项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);
  }

这有效,但在我看来它不具备可扩展性。我没有在这里看到任何简单或漂亮的修复,以动态地将类型传递给排序方法。关于此事的任何建议?

1 个答案:

答案 0 :(得分:0)

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

 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;