Я думаю, что причина ваших проблем в том, что вы получаете подмножества или вашу последовательность и заказываете это подмножество.Вы делаете это несколько раз и решаете составить списки всех промежуточных результатов.
Давайте сначала посмотрим, как вы хотите упорядочить своих учеников.
Итак, у вас есть schoolId
и последовательностьStudents
.Каждый Student
обладает свойствами SchoolId
, Message
и Status
Вы берете все Students
из школы с schoolId
, и по какой-то причине вы решаете назвать этих учеников studentMessages
.
Затем вы хотите заказать эти Students
(studentmessages) в следующем порядке:
- Сначала все учащиеся с нулевым сообщением и статусом, отличным от notActive, упорядочены по убыванию
UserId
- Затем все учащиеся с ненулевым сообщением и статусом, отличным от notActive, упорядочены по Message.NextFollowUpdate
- Затем все учащиеся с ненулевым сообщением и статусом, равным notActive, упорядочены по сообщению.NextFollowUpdate
- Наконец, все учащиеся с нулевым сообщением и статусом, равным notActive, упорядочены по убыванию
User.Id
(уверен, что вы не имели в виду UserId
? Думаю, это будет то же самое)
В таблице:
group | Message | Status | Order by
1 | == null | != notActive | descending UserId
2 | != null | != notActive | ascending message.NextFollowUpdate
3 | != null | == notActive | descending message.NextFollowUpdate
4 | == null | == notActive | ascending UserId
Один из методов - позволить вашей системе управления базами данных сделать это (AsQueryable).Алгоритм упорядочения кажется довольно сложным.Я не уверен, может ли СУБД сделать это более эффективно, чем ваш процесс.
Другим методом будет выборка только тех студентов, которые вам действительно нужны, и позволить вашему процессу выполнять упорядочение (AsEnumerable).Предоставьте класс, который реализует IComparer<Student>
для принятия решения о порядке.
int schoolId = ...
IComparer<Student> mySpecialStudentComparer = ...
var orderedStudents = dbContext.Students
.Where(student => student.SchoolId == schoolId)
.AsEnumerable() // move the selected data to local process
// now that the data is local, we can use our local Student Comparer
.OrderBy(mySpecialStudentComparer);
Если у вашего ученика есть много свойств, которые вы не будете использовать после извлечения данных, рассмотрите возможность создания локального класса, который содержиттолько те свойства, которые вам нужны и ограничивают выбранные данные этим локальным классом, например FetchedStudent
.Select(student => new FetchedStudent
{
// select only the properties you actually plan to use,
// for the sorting we need at least the following:
Message = student.Message,
Status = student.Status
UserId = student.UserId,
// Select the other Student properties you plan to use, for example:
Id = student.Id,
Name = student.Name,
...
}
Конечно, в этом случае ваш компаратор должен реализовать IComparer<FetchedStudent>
.
Итак, давайте создадим StudentComparer
, который будет сортировать учеников в соответствии с вашими требованиями!
class StudentComparer : IComparer<FetchedStudent>
{
private readonly IComparer<int> UserIdComparer = Comparer<int>.Default;
private readonly IComparer<DateTime> nextFollowUpdateComparer =
Comparer<DateTime>.Default;
public int CompareTo(FetchedStudent x, FetchedStudent y)
{
// TODO: decide what to do with null students: exception?
// or return as smallest or largest
// Case 1: check if x is in sorting group 1
if (x.Message == null && x.Status == notActive)
{
// x is in sorting group 1
if (y.Message == null && y.Status == notActive)
{
// x and y are in sorting group 1.
// order by descending UserId
return -UserIdComparer.CompareTo(x.UserId, y.UserId);
// the minus sign is because of the descending
}
else
{ // x is in group 1, y in group 2 / 3 / 4: x comes first
return -1;
}
}
// case 2: check if X is in sorting group 2
else if (x.Message != null && x.Status != notActive)
{ // x is in sorting group 2
if (y.Message == null && y.Status != notActive)
{ // x is in group 2; y is in group 1: x is larger than y
return +1;
}
else if (y.Message == null && y.Status != notActive)
{ // x and y both in group 2: order by descending nextFollowUpDate
// minus sign is because descending
return -nextFollowUpdateComparer.CompareTo(
x.Message.NextFollowUpdate,
y.Message.NextFollowUpdate);
}
else
{ // x in group 2, y in 3 or 4: x comes first
return -1;
}
}
// case 3: check if X in sorting group 3
else if (x.Message == null && x.Status != notActive)
{
... etc, you'll know the drill by know
}
}
Возможное улучшение
Вы видите, что компаратор постоянно сравнивает, равен ли x.Message нуль и равен ли x.Status равно notActive, чтобы определить, к какой группе сортировки принадлежат x и y.
Попробуйте создать функцию, которая только один раз вычисляет, к какой группе сортировки принадлежит студент, и запомните группу сортировки:
.Select(student => new FetchedStudent
{
SortingGroup = student.ToSortingGroup(),
... // other properties you need
}
public int CompareTo(FetchedStudent x, FetchedStudent y)
{
switch (x.SortingGroup)
{
case 1:
switch y.SortingGroup:
{
case 1: // x and y both in sorting group 1
return -UserIdComparer.CompareTo(x.UserId, y.UserId);
default: // x in sorting group 1, y in 2 / 3 / 4: x smaller
return -1;
}
case 2:
switch y.SortingGroup:
{
case 1: // x in sorting group 2; y in sorting group 1: x larger
return +1;
case 2: // x and y both in sorting group 2
return -nextFollowUpdateComparer.CompareTo(
x.Message.NextFollowUpdate,
y.Message.NextFollowUpdate);
}
и т.д..Таким образом, сравнение с сообщением и статусом выполняется только один раз