Использовать OrderBy в предикате LINQ?

#c# #linq #predicate

#c# #linq #предикат

Вопрос:

В моем коде мне нужно отсортировать коллекцию либо по цене, либо по рейтингу.TotalGrade и, как вы можете видеть, оба запроса LINQ представляют собой почти один и тот же оператор с незначительной разницей.

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

 if (CurrentDisplayMode == CRSChartRankingGraphDisplayMode.Position)
{
    this.collectionCompleteSorted = new List<Result>(from co in collection
                                    where co.IsVirtual == false
                                    orderby co.Price, co.CurrentRanking
                                    select co);
}
else if (CurrentDisplayMode == CRSChartRankingGraphDisplayMode.Grade)
{
    this.collectionCompleteSorted = new List<Result>(from co in collection
                                    where co.IsVirtual == false
                                    orderby co.Rating.TotalGrade, co.CurrentRanking
                                    select co);
}
  

Ответ №1:

Вы можете легко использовать дифференцированный характер LINQ и способность легко составлять запросы.

Вероятно, используя код, подобный этому:

         var baseQuery = from co in collection where !co.IsVirtual select co; // base of query
        IOrderedEnumerable<Result> orderedQuery; // result of first ordering, must be of this type, so we are able to call ThenBy

        switch(CurrentDisplayMode) // use enum here
        { // primary ordering based on enum
            case CRSChartRankingGraphDisplayMode.Position: orderedQuery = baseQuery.OrderBy(co => co.Price);
                break;
            case CRSChartRankingGraphDisplayMode.Grade: orderedQuery = baseQuery.OrderBy(co => co.TotalGrade);
                break;
        }

        this.collectionCompleteSorted = orderedQuery.ThenBy(co => co.CurrentRanking).ToList(); // secondary ordering and conversion to list
  

Это легко понять и позволяет избежать преобразования в список до самого конца.

Комментарии:

1. Работает отлично! Спасибо! Раннее преобразование в список — это плохо?

2. Это неплохо. Но вы не сможете использовать вторичный порядок с помощью ThenBy, если слишком рано преобразуете в List. Кроме того, если вы используете Linq для SQL или Entities, то это приведет к запросу, который полностью преобразуется в SQL. Если бы вы преобразовали в list раньше, упорядочивание было бы выполнено на клиенте.

Ответ №2:

Если отличается только ваш order by , верните ваш результат в this.collectionCompleteSorted , а затем выполните this.collectionCompleteSorted .OrderBy() при перечислении данных.

 this.collectionCompleteSorted = new List<Result>(from co in collection 
                                                             where co.IsVirtual == false                                                                  
                                                             select co);

foreach (var obj in this.collectionCompleteSorted.OrderBy(c => c.Price).ToList())
{
    // do something
}
  

И удалите порядок, удалив ваш текущий запрос linq.

Если запрос выполняется только один раз, то вы можете отказаться от .ToList() из запроса linq в примере выше. Когда вызывается ToList , это приводит к немедленному выполнению запроса, где, если OrderBy реализован в более позднем вызове коллекции, и ToList также, тогда запрос фактически будет выполнен на сервере базы данных с помощью инструкции order by, выгружающей порядок из кода в базу данных. См http://blogs.msdn.com/b/charlie/archive/2007/12/09/deferred-execution.aspx

Комментарии:

1. @Euphoric О чем ты говоришь? Вопрос был о том, как избежать того, чтобы не вводить order by в запрос linq. Я сказал использовать вызов метода OrderBy для коллекции, что, как ни странно, является тем же самым, что вы предлагаете сделать ниже.

2. Итак, где ваш вариант другого порядка, основанного на значении enum? Наряду со вторичным упорядочением.

Ответ №3:

Почему бы просто не захватить значения (за вычетом сортировки), а затем (как представляется) использовать регистр для упорядочивания результатов?

 // build your collection first
var items = from co in collection
            where !co.IsVirtual
            select co;

// go through your sort selectors
select (CurrentDisplayMode)
{
  case CRSChartRankingGraphDisplayMode.Position:
    this.collectionCompleteSorted = items.OrderBy(i => i.Price).ThenBy(j => j.CurrentRanking).ToList();
    break;
  case CRSChartRankingGraphDisplayMode.Grade:
    this.collectionCompleteSorted = items.OrderBy(i => i.TotalGrade).ThenBy(j => j.CurrentRanking).ToList();
    break;
  ...
  //default: // maybe you want this, too
}
  

Комментарии:

1. Почти готово, я бы рекомендовал удалить ToList из базового запроса и выполнить ToList после orderby. Кроме того, вы должны снова сохранить результат вашего OrderBy в элементе, то есть: this.collectionCompleteSorted = this.collectionCompleteSorted. OrderBy(…).ThenBy(…)

2. @Polity: Согласен. Я отложил отправку, все еще работая над кодом. 😉 Uno memento, por favor. ;p

Ответ №4:

 var q = collection.Where(co => !co.IsVirtual);

if (CurrentDisplayMode == CRSChartRankingGraphDisplayMode.Position)
{
    q = q.OrderBy(co => co.Price).ThenBy(co => co.CurrentRanking);
}
else if (CurrentDisplayMode == CRSChartRankingGraphDisplayMode.Grade)
{
    q = q.OrderBy(co => co.Rating.TotalGrade).ThenBy(co => co.CurrentRanking);
}

this.collectionCompleteSorted = q.ToList();
  

Комментарии:

1. Вы не сможете вызвать ThenBy для q, потому что q будет выведен из IEnumerable<>, а не IOrderedEnumerable<>