你在关注SQL方面,但是你也可以用纯EF做同样的事情。
下次提供EF代码会更有帮助,这样我们可以给你提供更具体的答案。
注意:在存在大量数据集的情况下,不要在EF中使用此逻辑,因为ReOrder过程会将所有记录加载到内存中。但是,在由附加过滤器子句范围限定的子列表或子列表中管理序数时,它非常有用(因此不适用于整个表!)
如果您需要在整个表中进行唯一排名逻辑,则孤立的ReOrder过程本身就是一个很好的候选项,可以作为存储过程去访问数据库。
这里有两个主要变化(用于唯一值):
- 排名必须始终是连续的
- 这简化了插入和替换逻辑,但您可能需要在代码中管理添加、插入、交换和删除场景。
- 编写代码以在排名中上下移动项目非常容易实现
- 必须管理删除以重新计算所有项目的排名
- 排名可以有间隔(不是所有值都是连续的)
- 这听起来应该更容易,但要评估在列表中向上和向下移动意味着您必须考虑到间隔。
我不会发布此变体的代码,但请注意,通常更难维护。
- 另一方面,您无需担心主动管理删除。
当需要管理序数时,我使用以下例程。
注意:此例程不保存更改,它只是将可能受到影响的所有记录加载到内存中,以便我们可以正确处理新的排名。
public static void ReOrderTableRecords(Context db)
{
var currentValues = db.Table.ToList()
.Where(x => db.Entry(x).State != EntityState.Deleted)
.OrderBy(x => x.Rank);
int order = 1;
foreach (var item in currentValues)
item.Order = order++;
}
假设你可以将代码简化为一个函数,该函数将具有特定排名的新项目插入列表中,或者你想交换列表中两个项目的排名:
public static Table InsertItem(Context db, Table item, int? Rank = 1)
{
if (Rank.HasValue)
item.Rank = Rank;
if (item.Rank <= 0)
item.Rank = 1;
ReOrderTableRecords(db);
var items = db.Table.ToList()
.Where(x => db.Entry(x).State != EntityState.Deleted)
.Where(x => x.Rank >= item.Rank);
if (items.Any())
{
foreach (var i in items)
i.Rank = i.Rank + 1;
}
else if (item.Rank > 1)
{
item.Rank = db.Table.ToList()
.Where(x => db.Entry(x).State != EntityState.Deleted)
.Max(x => x.Rank) + 1;
}
db.Table.Add(item);
db.SaveChanges();
return item;
}
public static void UpdateRank(Context db, Table item)
{
var rank = item.Rank;
item.Rank = -1;
ReOrderTableRecords(db);
var items = db.Table.ToList()
.Where(x => db.Entry(x).State != EntityState.Deleted)
.Where(x => x.Rank >= rank);
if (items.Any())
{
foreach (var i in items)
i.Rank = i.Rank + 1;
}
item.Rank = rank;
db.SaveChanges();
}
public static void SwapItemsByIds(Context db, int item1Id, int item2Id)
{
var item1 = db.Table.Single(x => x.Id == item1Id);
var item2 = db.Table.Single(x => x.Id == item2Id);
var rank = item1.Rank;
item1.Rank = item2.Rank;
item2.Rank = rank;
db.SaveChanges();
}
public static void MoveUpById(Context db, int item1Id)
{
var item1 = db.Table.Single(x => x.Id == item1Id);
var rank = item1.Rank - 1;
if (rank > 0)
{
var item2 = db.Table.Single(x => x.Rank == rank);
item2.Rank = item1.Rank;
item1.Rank = rank;
db.SaveChanges();
}
}
public static void MoveDownById(Context db, int item1Id)
{
var item1 = db.Table.Single(x => x.Id == item1Id);
var rank = item1.Rank + 1;
var item2 = db.Table.SingleOrDefault(x => x.Rank == rank);
if (item2 != null)
{
item2.Rank = item1.Rank;
item1.Rank = rank;
db.SaveChanges();
}
}
为确保不会引入间隙,您应该在从表格中删除项目之后但在调用
SaveChanges()
之前调用
ReOrder
。
或者,在每次调用 Swap/MoveUp/MoveDown 之前类似于插入操作一样调用
ReOrder
。
请记住,允许重复的排名值会更简单,尤其是对于大量数据的列表,但您的业务需求将决定这是否是可行的解决方案。