我认为你的问题的原因是因为你得到了子集或你的序列并订购了这个子集。您这样做了好几次,然后决定列出所有中间结果。
让我们先看看你想如何安排你的学生。
所以你有一个schoolId 和一个Students 的序列。每个Student 都有属性SchoolId、Message 和Status
你把学校里的所有Students 和schoolId 联系起来,出于某种原因你决定给这些学生打电话studentMessages。
那么您想按以下顺序订购这些Students(学生消息):
- 首先是所有消息为空且状态不等于 notActive 的学生,按降序排列
UserId
- 然后所有具有非空消息且状态不等于 notActive 的学生,按 Message.NextFollowUpdate 排序
- 然后所有具有非空消息且状态等于 notActive 的学生,按 Message.NextFollowUpdate 排序
- 最后,所有消息为 null 且状态等于 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)。排序算法似乎相当复杂。我不确定 DBMS 是否可以比您的流程更有效地做到这一点。
另一种方法是仅获取您实际需要的学生并让您的进程进行排序 (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);
如果您的 Student 有很多属性在您获取数据后不会使用,请考虑创建一个仅包含您需要的属性的本地类,并将所选数据限制为该本地类,例如 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是否等于null,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);
}
等等。这样与 Message 和 Status 的比较只进行一次