Вопрос по c#, linq, lambda, expression-trees – Создание лямбда-выражения OrderBy на основе свойства дочерней сущности

11

Я пытаюсь сгенерировать LINQOrderBy предложение с использованием лямбда-выражений с вводом имени столбца сущности в виде строки (в переменной «quot; sortOn» ниже).

Приведенный ниже код отлично работает для значения sortOn, например & quot; Код & quot; генерируя лямбду

p => p.Code

Но я также хотел бы отсортировать по дочернему объекту, где лямбда может быть

p => p.Category.Description

Так что в этом случае я просто хотел бы установить sortOn = & Category.escription & quot; и иметь правильное выражение lamdba.

Это возможно? Любые предложения о том, как это сделать, будут приветствоваться.

Этот код отлично работает для простого случая:

var param = Expression.Parameter(typeof (Product), "p");

var sortExpression = Expression.Lambda<Func<Product, object>>(
    Expression.Property(param, sortOn), param);

if (sortAscending ?? true)
{
   products = products.OrderBy(sortExpression);
}
else
{
   products = products.OrderByDescending(sortExpression);
}

Вариант использования для этой проблемы - отображение сетки данных и возможность сортировки данных, просто передавая имя столбца для сортировки обратно на сервер. Я хотел бы сделать решение универсальным, но сейчас начал использовать конкретный тип (в данном примере Product).

Почему вы вручную создаете выражения? Peter Ritchie

Ваш Ответ

5   ответов
0

products = products.Orderby(p1 => p1.Code).ThenBy(p2 => p2.Category.Description)
0

вы также можете создать метод расширения, который может сортировать на любую глубину не только ребенка

        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);

    }

}

Это пользовательский метод, и сейчас он работает только для строкового свойства, однако его можно повторно использовать с помощью обобщений для работы с любым примитивным типом. Я надеюсь, это поможет.

0

OrderBy метод, который работает для любого количества вложенных параметров.

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;
  }
}

CreateExpression Метод, который используется в моем решении определяетсяв этом посте.

ИспользованиеOrderBy Способ расширения заключается в следующем.

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();

ТипFoo и его свойства также взяты из того же поста, что и методCreateExpression.

Надеюсь, вы найдете этот пост полезным.

2

Динамическая библиотека запросов LINQ сделать это легко. Если у вас естьIQueryable<T> implementation изProduct, вы можете легко сделать:

IQueryable<Product> products = ...;

// Order by dynamically.
products = products.OrderBy("Category.Description");

В блоге естьссылка на библиотекуи вам придется самостоятельно построить / включить проект в свое решение, но он работает очень хорошо, и синтаксический анализ очень надежен. Это избавляет вас от необходимости самостоятельно писать код анализа; даже для чего-то такого простого, если требования расширяются, библиотека покрывает вас, в то время как доморощенное решение - нет.

Он также имеет ряд других динамических операторов (Select, Whereи т. д.), чтобы вы могли выполнять другие динамические операции.

Под капотом нет никакой магии, он просто анализирует строки, которые вы передаете, а затем создает лямбда-выражения на основе результатов анализа.

Спасибо за предложение. Потенциально пометил бы как ответ, но Томек дал мне подход, который я хотел. Обязательно рассмотрим это как альтернативное решение. Appetere
18

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);

Похожие вопросы