Building an OrderBy Lambda expression based on child entity's property

后端 未结 5 1737
渐次进展
渐次进展 2020-12-10 04:34

I\'m trying to generate a LINQ OrderBy clause using lambda expressions with an input of the column name of an entity as a string (in the \"sortOn\" variable bel

相关标签:
5条回答
  • 2020-12-10 04:43

    This will generate proper lambda expression:

    var sortOn = "Category.Description";
    var param = Expression.Parameter(typeof(Product), "p");
    var parts = sortOn.Split('.');
    
    Expression parent = param;
    
    foreach (var part in parts)
    {
        parent = Expression.Property(parent, part);
    }
    
    var sortExpression = Expression.Lambda<Func<Product, object>>(parent, param);
    
    0 讨论(0)
  • 2020-12-10 04:52

    Hi you can also create an extension method like which can sort to any depth not only just child

            public static IEnumerable<TSource> CustomOrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
        {
            List<string> list=new List<string>();
            List<TSource> returnList=new List<TSource>();
            List<int> indexList = new List<int>();
    
            if (source == null)
                return null;
            if (source.Count() <= 0)
                return source;
            source.ToList().ForEach(sc=>list.Add(keySelector(sc).ToString())); //Extract the strings of property to be ordered
    
            list.Sort(); //sort the list of strings
    
            foreach (string l in list) // extract the list of indexes of source according to the order
            {
                int i=0;
                //list.ForEach(l =>
    
                    foreach (var s in source.ToList())
                    {
                        if (keySelector(s).ToString() == l)
                            break;
                        i++;
                    }
                    indexList.Add(i);
            }
            indexList.ForEach(i=>returnList.Add(source.ElementAt(i))); //rearrange the source according to the above extracted indexes
            return returnList;
        }
    }
    public class Name
    {
        public string FName { get; set; }
        public string LName { get; set; }
    }
    public class Category
    {
        public Name Name { get; set; }
    }
    public class SortChild
    {
        public void SortOn()
        {
            List<Category> category = new List<Category>{new Category(){Name=new Name(){FName="sahil",LName="chauhan"}},
                new Category(){Name=new Name(){FName="pankaj",LName="chauhan"}},
                new Category(){Name=new Name(){FName="harish",LName="thakur"}},
                new Category(){Name=new Name(){FName="deepak",LName="bakseth"}},
                new Category(){Name=new Name(){FName="manish",LName="dhamaka"}},
                new Category(){Name=new Name(){FName="arev",LName="raghaka"}}
            };
            var a = category.CustomOrderBy(s => s.Name.FName);
    
        }
    
    }
    

    Its custom method and right now it works only for string property only however it can be reactified using generics to work for any primitive type. I hope this will help.

    0 讨论(0)
  • 2020-12-10 04:59

    Here is an extension OrderBy method which works for any number of nested parameters.

    public static IQueryable<T> OrderBy<T>(this IQueryable<T> query, string key, bool asc = true)
    {
      try
      {
        string orderMethodName = asc ? "OrderBy" : "OrderByDescending";
        Type type = typeof(T);
        Type propertyType = type.GetProperty(key)?.PropertyType; ;
    
        var param = Expression.Parameter(type, "x");
        Expression parent = param;
    
        var keyParts = key.Split('.');
        for (int i = 0; i < keyParts.Length; i++)
        {
          var keyPart = keyParts[i];
          parent = Expression.Property(parent, keyPart);
    
          if (keyParts.Length > 1)
          {
            if (i == 0)
            {
              propertyType = type.GetProperty(keyPart).PropertyType;
            }
            else
            {
              propertyType = propertyType.GetProperty(keyPart).PropertyType;
            }
          }
        }
    
        MethodCallExpression orderByExpression = Expression.Call(
          typeof(Queryable),
          orderMethodName,
          new Type[] { type, propertyType },
          query.Expression,
          CreateExpression(type, key)
        );
    
        return query.Provider.CreateQuery<T>(orderByExpression);
      }
      catch (Exception e)
      {
        return query;
      }
    }
    

    The CreateExpression method which is used in my solution is defined in this post.

    The usage of the OrderBy extension method is as follows.

    IQueryable<Foo> q = [Your database context].Foos.AsQueryable();
    IQueryable<Foo> p = null;
    
    p = q.OrderBy("myBar.name");  // Ascending sort
    // Or
    p = q.OrderBy("myBar.name", false);  // Descending sort
    // Materialize
    var result = p.ToList();
    

    The type Foo and its properties are also taken from the same post as method CreateExpression.

    Hope you find this post helpful.

    0 讨论(0)
  • 2020-12-10 05:04

    You can use the Dynamic LINQ Query Library to do this easily. Assuming you have an IQueryable<T> implementation of Product, you can easily do:

    IQueryable<Product> products = ...;
    
    // Order by dynamically.
    products = products.OrderBy("Category.Description");
    

    The blog post has a link to the libary, and you'll have to build/include the project in your solution yourself, but it works very well, and the parsing is very robust. It prevents you from having to write the parsing code yourself; even for something so simple, if the requirements expand, the library has you covered, whereas a homegrown solution does not.

    It also has a number of other dynamic operators (Select, Where, etc.) so you can perform other dynamic operations.

    There's no magic under the hood, it just parses the strings you pass it and then creates the lambda expressions based on the parsing results.

    0 讨论(0)
  • 2020-12-10 05:06

    If you don't need expressions, how about:

    products = products.Orderby(p1 => p1.Code).ThenBy(p2 => p2.Category.Description)
    
    0 讨论(0)
提交回复
热议问题