我有一個系統,它應該基于從另一個應用程式提供的排序資料串列創建排序運算式。排序資訊作為包含排序鍵 (Object.thingtosort) 和方向的物件給出。
我當前的解決方案是圍繞我從另一個堆疊溢位問題中獲得的解決方案構建的,在這里找到:原始問題。每當我運行以下代碼時:
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;
}
我得到錯誤:
System.ArgumentException: 'Expression of type 'System.Int32' cannot be used for return type 'System.Object''
我嘗試通過使用將引數轉換為物件來解決這個問題
Expression.Convert(body, typeof(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 convertedBody = Expression.Convert(body, typeof(object));
var func = Expression.Lambda<Func<T, object>>(convertedBody, parameter); //<-- line of error
return func;
}
這創建了另一個問題,它無法比較陣列中的兩個元素(可能是因為在這種情況下它不知道如何將物件與物件進行比較)。
System.InvalidOperationException: 'Failed to compare two elements in the array.'
ArgumentException: At least one object must implement IComparable.
我希望它適用于任何型別,因為排序應該適用于我的物件的任何欄位(這些可以嵌套多層深)。下面包括重新創建我的問題所需的完整代碼。
Microsoft Visual Studio Community 2022(64 位)- 預覽版 17.3.0 預覽版 1.1
該專案是一個使用 .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; }
}
uj5u.com熱心網友回復:
看來這最終是一個小錯誤。
- 您正在嘗試按
Expression<Func<T, object>>( 的值進行排序,而不是randomNumber確定排序順序的值。 .OrderBy對于 enumerables (not queryables) 期望Func<TSource, TKey>whereTSourceisOrderEntity并且在這種情況下TKey應該是您的值。object
所以我們需要做兩件事:
- 編譯運算式。
- 使用它進行排序。
本質上我們需要這個
Func<OrderEntity, object> sortAccessor = ToLambda<OrderEntity>(propertyForExpression).Compile();
var sortedOrders = orders.OrderBy(sortAccessor);
或者
Func<OrderEntity, object> sortAccessor = ToLambda<OrderEntity>(propertyForExpression).Compile();
var sortedOrders = orders.OrderBy(x => sortAccessor(x));
或者
var sortedOrders = orders.OrderBy(x => ToLambda<OrderEntity>(propertyForExpression).Compile()(x));
或者
var sortedOrders = orders.OrderBy(ToLambda<OrderEntity>(propertyForExpression).Compile());
您還可以更改方法以回傳已編譯的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;
}
然后更像這樣使用它:
Func<OrderEntity, object> sortAccessor = ToLambda<OrderEntity>(propertyForExpression);
var sortedOrders = orders.OrderBy(sortAccessor);
或這個:
var sortedOrders = orders.OrderBy(ToLambda<OrderEntity>(propertyForExpression));
注意:我建議在回圈之外編譯運算式并將其快取為Func<OrderEntity, object>變數的原因是因為否則它將為單個.OrderBy.
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/490587.html
下一篇:結構與類中欄位的聯鎖更新
