[英]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.看起来这最终是一个小错误。
Expression<Func<T, object>>
(as in the value of that, not the value of randomNumber
determines the sort order.Expression<Func<T, object>>
进行排序(因为它的值,而不是randomNumber
的值决定排序顺序。.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>
,其中TSource
是OrderEntity
,在这种情况下TKey
应该是您的object
值。 So we need to do two things:所以我们需要做两件事:
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.