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.
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
Expression.Convert(body, typeof(object));
Resulting in the following 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).
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). 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
The project is a console application using .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; }
}