简体   繁体   English

从字符串构建 Linq 排序表达式会导致“Expression of "system.int32" can not be used for return type "System.Object"”

[英]Building a Linq sort expression from string results in 'Expression of "system.int32" can not be used for return type "System.Object" '

I have a system that should create a sort expression based on a list of sort data that is supplied from another application.我有一个系统应该根据另一个应用程序提供的排序数据列表创建排序表达式。 The sort info is given as an object that contains a sort key (Object.thingtosort) and a direction.排序信息以 object 形式给出,其中包含排序键 (Object.thingtosort) 和方向。

My current solution was built around a solution I got from another stack overflow question, found over here: original question .我当前的解决方案是围绕我从另一个堆栈溢出问题中获得的解决方案构建的,在此处找到: 原始问题 Whenever I run the following code:每当我运行以下代码时:

    static Expression<Func<T, object>> ToLambda<T>(string propertyName)
    {
        var propertyNames = propertyName.Split('.');
        var parameter = Expression.Parameter(typeof(T));
        Expression body = parameter;
        foreach (var propName in propertyNames)
            body = Expression.Property(body, propName);
        var func = Expression.Lambda<Func<T, object>>(body, parameter); //<-- line of error
        return func;
    }

I get the error:我收到错误:

System.ArgumentException: 'Expression of type 'System.Int32' cannot be used for return type 'System.Object''

I tried solving this by converting the parameter to an Object by using我尝试通过使用将参数转换为 Object 来解决此问题

    Expression.Convert(body, typeof(object));

Resulting in the following function:导致以下 function:

static Expression<Func<T, object>> ToLambda<T>(string propertyName)
    {
        var propertyNames = propertyName.Split('.');
        var parameter = Expression.Parameter(typeof(T));
        Expression body = parameter;
        foreach (var propName in propertyNames)
            body = Expression.Property(body, propName);
        var convertedBody = Expression.Convert(body, typeof(object));
        var func = Expression.Lambda<Func<T, object>>(convertedBody, parameter); //<-- line of error
        return func;
    }

This Creates creates another problem where it fails to compare two elements in the array (probably because it doesn't know how to compare object to object in this case).这创建了另一个问题,它无法比较数组中的两个元素(可能是因为在这种情况下它不知道如何比较 object 和 object)。

System.InvalidOperationException: 'Failed to compare two elements in the array.'
ArgumentException: At least one object must implement IComparable.

I want it to work with any type, as the sorting should work on any field of my object (these can be nested multiple layers deep).我希望它适用于任何类型,因为排序应该适用于我的 object 的任何字段(这些可以嵌套多层)。 Included below is the full code required to recreate my problem.下面包含的是重新创建我的问题所需的完整代码。

Microsoft Visual Studio Community 2022 (64-bit) - Preview Version 17.3.0 Preview 1.1 Microsoft Visual Studio Community 2022(64 位)- 预览版 17.3.0 预览版 1.1

The project is a console application using .net 6.该项目是一个控制台应用程序,使用 .net 6。


    using System.Linq.Expressions;

    internal class Program
    {
        private static void Main(string[] args)
        {
            Console.WriteLine("Hello, World!");

            string propertyForExpression = "Product.randomNumber";

            Random random = new Random();

            List<OrderEntity> orders = new List<OrderEntity>();

            for (int i = 1; i < 11; i++)
            {
                var orderToAdd = new OrderEntity();
                orderToAdd.id = i;
                orderToAdd.name = "order number " + i;

                var productToAdd = new ProductEntity();
                productToAdd.id = i;
                productToAdd.productName = "product " + i;
                productToAdd.description = "this is a product";
                productToAdd.randomNumber = random.Next(1, 100);

                orderToAdd.Product = productToAdd;
                orders.Add(orderToAdd);
            }

            var sortedOrders = orders.OrderBy(X => ToLambda<OrderEntity> (propertyForExpression));

            foreach(var order in sortedOrders)
            {
                Console.WriteLine(order.Product.randomNumber);
            }
            Console.ReadKey();
        }

        static Expression<Func<T, object>> ToLambda<T>(string propertyName)
        {
            var propertyNames = propertyName.Split('.');
            var parameter = Expression.Parameter(typeof(T));
            Expression body = parameter;
            foreach (var propName in propertyNames)
                body = Expression.Property(body, propName);
            var func = Expression.Lambda<Func<T, object>>(body, parameter);
            return func;
        }


    // ToLambda function that crashes on the OrderBy with error: System.InvalidOperationException: 'Failed to compare two elements in the array.'

    //static Expression<Func<T, object>> ToLambda<T>(string propertyName)
    //{
    //    var propertyNames = propertyName.Split('.');
    //    var parameter = Expression.Parameter(typeof(T));
    //    Expression body = parameter;
    //    foreach (var propName in propertyNames)
    //        body = Expression.Property(body, propName);
    //    var convertedBody = Expression.Convert(body, typeof(object));
    //    var func = Expression.Lambda<Func<T, object>>(convertedBody, parameter);
    //    return func;
    //}
    }


    public class OrderEntity
    {
        public int id { get; set; }
        public string name { get; set; }
        public ProductEntity Product { get; set; }
    }

    public class ProductEntity
    {
        public int id { get; set; }
        public string productName { get; set; }
        public string description { get; set; }
        public int randomNumber { get; set; }
    }

It seems like this is ultimately a minor mistake.看起来这最终是一个小错误。

  1. You're trying to order by the Expression<Func<T, object>> (as in the value of that, not the value of randomNumber determines the sort order.您正在尝试按Expression<Func<T, object>>进行排序(因为它的值,而不是randomNumber的值决定排序顺序。
  2. .OrderBy for enumerables (not queryables) expects a Func<TSource, TKey> where TSource is OrderEntity and TKey is supposed to be your object value in this case. .OrderBy对于可枚举项(不是可查询项)需要一个Func<TSource, TKey> ,其中TSourceOrderEntity ,在这种情况下TKey应该是您的object值。

So we need to do two things:所以我们需要做两件事:

  1. Compile the expression.编译表达式。
  2. Use it for sorting.用它来排序。

Essentially we need this本质上我们需要这个

Func<OrderEntity, object> sortAccessor = ToLambda<OrderEntity>(propertyForExpression).Compile();
var sortedOrders = orders.OrderBy(sortAccessor);

or或者

Func<OrderEntity, object> sortAccessor = ToLambda<OrderEntity>(propertyForExpression).Compile();
var sortedOrders = orders.OrderBy(x => sortAccessor(x));

or或者

var sortedOrders = orders.OrderBy(x => ToLambda<OrderEntity>(propertyForExpression).Compile()(x));

or或者

var sortedOrders = orders.OrderBy(ToLambda<OrderEntity>(propertyForExpression).Compile());

You could also change the method to return the compiled Func<T, object> instead:您还可以更改方法以返回编译后的Func<T, object>

static Expression<Func<T, object>> ToLambda<T>(string propertyName)
{
    var propertyNames = propertyName.Split('.');
    var parameter = Expression.Parameter(typeof(T));
    Expression body = parameter;
    foreach (var propName in propertyNames)
        body = Expression.Property(body, propName);

    var convertedResult = Expression.Convert(body, typeof(object));

    var func = Expression.Lambda<Func<T, object>>(convertedResult, parameter);
    return func;
}

and then use it more like this:然后像这样使用它:

Func<OrderEntity, object> sortAccessor = ToLambda<OrderEntity>(propertyForExpression);
var sortedOrders = orders.OrderBy(sortAccessor);

or this:或这个:

var sortedOrders = orders.OrderBy(ToLambda<OrderEntity>(propertyForExpression));

Note: the reason I'm suggesting compiling the expression outside of the loop and caching it as a Func<OrderEntity, object> variable is because otherwise it will be evaluated multiple times for a single .OrderBy .注意:我建议在循环外编译表达式并将其缓存为Func<OrderEntity, object>变量的原因是因为否则它将针对单个.OrderBy进行多次计算。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Linq和Equality Operator:类型'System.Int32'的表达式不能用于'System.Object'类型的参数 - Linq and the Equality Operator: Expression of type 'System.Int32' cannot be used for parameter of type 'System.Object' 类型'System.Int32'的表达式不能用于返回类型'System.Object' - Expression of type 'System.Int32' cannot be used for return type 'System.Object' 类型'System.Int32'的表达式不能用于方法'Boolean Equals(System.Object)'的'System.Object'类型的参数 - Expression of type 'System.Int32' cannot be used for parameter of type 'System.Object' of method 'Boolean Equals(System.Object)' 类型&#39;System.Int32&#39;的表达式不能用于方法&#39;Boolean Equals(System.Object)&#39;的类型&#39;System.Object&#39;的参数 - Expression of type 'System.Int32' cannot be used for parameter of type 'System.Object' of method 'Boolean Equals(System.Object)' &#39;System.Int16&#39;类型的表达式不能用于返回类型&#39;System.Object&#39; - Expression of type 'System.Int16' cannot be used for return type 'System.Object' “System.Int64”类型的表达式不能用于返回类型“System.Object” - Expression of type 'System.Int64' cannot be used for return type 'System.Object' 无法将类型“ System.Int32”强制转换为类型“ System.Object”。 LINQ to Entities仅支持强制转换EDM基本类型或枚举类型 - Unable to cast the type 'System.Int32' to type'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types Expression.Convert:'System.Int64'类型的对象无法转换为'System.Int32'类型 - Expression.Convert: Object of type 'System.Int64' cannot be converted to type 'System.Int32' System.InvalidCastException:无法将类型为“System.Linq.OrderedEnumerable`2[System.String,System.Int32]”的 object 转换为类型 [System.Int32]' - System.InvalidCastException : Unable to cast object of type 'System.Linq.OrderedEnumerable`2[System.String,System.Int32]' to type [System.Int32]' 在LINQ查询中无法将类型为&#39;System.Int32&#39;的对象转换为类型为&#39;System.String&#39;的对象 - Unable to cast object of type 'System.Int32' to type 'System.String' in LINQ query
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM