简体   繁体   中英

Sort List by Object type property using extension method - Asp.Net Core 6

I have created an API using ASP.Net Core 6. This API used to manipulate the store products. The getAllProducts endpoint returns the list of products and I have added the sorting functionality to the getAllProducts(list) endpoint by using an extension method.

Product Entity:

public class Product
{
    [Required]
    [StringLength(50)]
    public string Name { get; set; }
    public string Description { get; set; }
    public decimal Price { get; set; }  
    public virtual ProductCategory Category { get; set; }
}

ProductCategory entity:

public class ProductCategory
{
    public string Name { get; set; }
}

Extension method:

public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> list, string orderByProperty, bool desc)
    {
        string command = desc ? "OrderByDescending" : "OrderBy";
        var type = typeof(TEntity);
        var property = type.GetProperty(orderByProperty);
        var parameter = Expression.Parameter(type, "p");
        var propertyAccess = Expression.MakeMemberAccess(parameter, property);
        var orderByExpression = Expression.Lambda(propertyAccess, parameter);
        var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType },
            list.Expression, Expression.Quote(orderByExpression));
        return list.Provider.CreateQuery<TEntity>(resultExpression);
    }

I called the extension method this way.

productsList = productsList.AsQueryable().OrderBy("Category", true).ToList();

This works fine with all product entity properties except the 'Category'. It throws an exception System.InvalidOperationException: Failed to compare two elements in the array. when I pass 'Category' as orderByProperty. I want to customize the extension method so that if an object type property passed in, sort the list by name of the passed object. (by category name in this case). I expect your help. Thank You.

Runtime does not know how to compare two instances of ProductCategory so you need to provide a way for LINQ-to-Objects to do this. For example implementing IComparable<ProductCategory> :

public class ProductCategory : IComparable<ProductCategory>
{
    public string Name { get; set; }

    public int CompareTo(ProductCategory? other)
    {
        if (ReferenceEquals(this, other)) return 0;
        if (ReferenceEquals(null, other)) return 1;
        return string.Compare(Name, other.Name, StringComparison.Ordinal);
    }
}

Also possibly you can consider passing expression into the method:

public static IQueryable<TEntity> OrderBy<TEntity, TProp>(this IQueryable<TEntity> list, Expression<Func<TEntity, TProp>> selector, bool desc)
{
   // change accordingly 
}

With usage changed to:

productsList = productsList.AsQueryable()
    .OrderBy(p => p.Category.Name, true)
    .ToList();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM