0

I have IEnumerable<Cars> variable to which I'm assigning values from the database.

I would like to apply sorting based on the user's input to the above variable.

I have columns like in the UI i.e. Car Model, Car Model's year, Number of car sold out, Number of car in stock. I have implemented multiple sorting on the table means user can select a column and based on that he can sort the other column.

In this case, if user selects Car Model in ascending order and he will get the results based on the selection and now when he selects the next column i.e year, the previous order shouldn't change.

It is kind of Orderby().ThenBy();

I have foreach in which I have implemented switch case

switch(field) //field prop will come from foreach
{
    case "CarModel":
        objCars = sortMember.Dir.ToUpperInvariant() == "ASC" ?
        objCars.OrderBy(x => x.CarModel).ToList() : 
        objCars.OrderByDescending(x => x.CarModel).ToList();
        break;

    case "CarYear":
        objCars = sortMember.Dir.ToUpperInvariant() == "ASC" ? 
        objCars.OrderBy(x => x.CarYear).ToList() : 
        objCars.OrderByDescending(x => x.CarYear).ToList();
        break;
}

I also tried to implement using IOrderedEnumerable. During debugging I found that sorting is happening based on the input when we IOrderedEnumberable but finally when I'm assigning back to the IEnumerable variable, I'm losing the order.

Assuming user selects the 'Car Model column' as the first sorting item,

switch(field)
{
    case "CarModel":
        objCars_Sorted = sortMember.Dir.ToUpperInvariant() == "ASC" ? 
        objCars.OrderBy(x => x.CarModel).ToList() : 
        objCars.OrderByDescending(x => x.CarModel).ToList();
        break;

    case "CarYear":
        objCars_Sorted = sortMember.Dir.ToUpperInvariant() == "ASC" ? 
        objCars_Sorted.OrderBy(x => x.CarYear).ToList() : 
        objCars_Sorted.OrderByDescending(x => x.CarYear).ToList();
        break;
}

objCars = objCars_Sorted.Skip(0).Take(10).Tolist() //I also tried objCars_Sorted.AsEnumerable but no luck

Above code return without the expected sorting i.e.

OrderBy(CarModel).ThenBy(CarYear). 

Is there any other way or any change needs to be done on my code above?

Please suggest.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
gkrishy
  • 756
  • 1
  • 8
  • 33
  • The code is very non-symmetrical (3 different sources used to get value that is presumably built up in some mysterious foreach loop) It is also not real as it is missing `break;` in `switch`... Could you please review what is posted in the question and make sure it is indeed [MCVE]? – Alexei Levenkov Dec 23 '19 at 04:20
  • Sorry. I posted in hurry so missed it. Thanks for pointing it out. – gkrishy Dec 23 '19 at 04:23
  • You may want to look at `LinqKit` (http://www.albahari.com/nutshell/linqkit.aspx), it often contains a way to play with LINQ expressions that can solve problems like this – Flydog57 Dec 23 '19 at 05:18

2 Answers2

1

You can do that with Build Expression.

I created extension method that can be implement for IQueryable as following:

public static IQueryable<T> OrderByField<T>(this IQueryable<T> q, string SortField, bool Ascending)
{
    var param = Expression.Parameter(typeof(T), "p");
    var prop = Expression.Property(param, SortField);
    var exp = Expression.Lambda(prop, param);
    string method = Ascending ? "OrderBy" : "OrderByDescending";
    Type[] types = new Type[] { q.ElementType, exp.Body.Type };
    var mce = Expression.Call(typeof(Queryable), method, types, q.Expression, exp);
    return q.Provider.CreateQuery<T>(mce);
}

Then you need to call

bool isAscending = sortMember.Dir.ToUpperInvariant() == "ASC"; 
objCars = objCars.OrderByField(field, isAscending);

It can be used in Foreach loop also

foreach(var field in sortFields)
{
     objCars = objCars.OrderByField(field, isAscending);
}
Selim Yildiz
  • 5,254
  • 6
  • 18
  • 28
  • Can you please let me know how can i declare this variable "objCars_Sorted" ? – gkrishy Dec 23 '19 at 06:26
  • It is actually your DB Context, I changed `objCars_Sorted` to `objCars`. Does this make sense now? – Selim Yildiz Dec 23 '19 at 06:29
  • I tried something like this, IQueryable objCars_Sorted = objCars.AsQueryable() and when I'm trying to do, objCars_Sorted.OrderByField is not working means I'm not getting OrderByField here. – gkrishy Dec 23 '19 at 06:31
  • `OrderByField` is a extension method that you have you write. You can copy directly from my answer then add to your project. – Selim Yildiz Dec 23 '19 at 06:33
  • Yes. The extension part is already done. I have copied your code and placed in a separate class and pasted like this, public static class QueryableExtensions { your code }. I also referred this class into my main class where I'm doing the above func. But I'm seeing orderby and orderbydescending and not this one. – gkrishy Dec 23 '19 at 06:37
  • I mean it has to shown since extension referred `this IQueryable q` , please make sure that you add namespace of Extension method. – Selim Yildiz Dec 23 '19 at 06:48
  • Sorry. It was my mistake. I'm able to see the OrderByField now. Thanks for your help. :-) – gkrishy Dec 23 '19 at 07:28
1

You can install System.Linq.Dynamic package which provides an OrderBy extension that takes a string.

For example:

using System.Linq.Dynamic;

var sortedList = source.OrderBy("Property1 ascending, Property2 descending").ToList();

In your case, you can then do this:

 string orderByCarModel = string.Format("Property1 {0}, Property2 {1}", "ascending", "descending");

Reference: LINQ select property by name

public static class ObjectReflectionExtensions
{
    public static  object GetValueByName<T>(this T thisObject,  string propertyName)
    {
        PropertyInfo prop = typeof(T).GetProperty(propertyName);
        return prop.GetValue(thisObject);

    }
}

Then you can have:

public static List<string> GetListOfProperty(IEnumerable<ClassNamehere> listOfObjects, string propertyName, string orderByClause)
{
    return listOfObjects.Select(x =>(string)x.GetValueByName(propertyName)).OrderBy(orderByClause).ToList();
}
Gauravsa
  • 6,330
  • 2
  • 21
  • 30