我正在尝试找出使表格任意排序的最佳方法。我的意思是用户可以更改表的排序顺序,而与数据无关。为了适应这一点,我在表中添加了一个名为 Rank
的新列,该列应该用于排序。
我写这个函数来改变给定实体的等级
public void ChangeRank(int id, int rank)
{
_dbSet.Find(id).Rank = rank;
// adjust ranks
var index = rank + 1;
foreach (var entity in _dbSet.Where(x => x.Rank >= rank && x.Id != id).OrderBy(x => x.Rank))
entity.Rank = index++;
_context.SaveChanges();
}
我相信,如果我错了,请纠正我,foreach 循环没有转换为我希望做的单个 sql 语句 - 为了性能。我很确定可以使用row_number()
在原始sql中执行此操作,但我想将其保留为LINQ查询。
如何优化排名更新?
有没有更好的方法来实现任意排序?
我会避免更新多个实体。
将Rank
设置为double
,每当需要更改排名时,请将其设置为接下来两条记录之间的值。例如,如果您想将实体向下移动:
public void MoveDown(int id)
{
var entity = _dbSet.Find(id);
var nextTwo = _dbSet.Where(x => x.Rank > entity.Rank)
.OrderBy(x => x.Rank)
.Select(x => x.Rank)
.Take(2).ToList();
if (nextTwo.Count == 2)
{
entity.Rank = (nextTwo[0] + nextTwo[1]) / 2;
}
// if entity is second last
else if (nextTwo.Count == 1)
{
entity.Rank = nextTwo[0] + 1;
}
_context.SaveChanges();
}
下移 ID 1 的示例结果
Before
ID Rank
1 1
2 2
3 3
After
ID Rank
1 2.5
2 2
3 3
如果要将实体设置为某个位置,则可以使用类似的方法,但想法是仅修改一个实体
我认为Find
比First
慢很多倍。其次,逻辑可以简化,所以你不需要排序:
var row = _dbSet.First(x => x.Id == id);
var oldRank = row.Rank;
if(oldRank == newRank)
return;
if(newRank > oldRank)
{
foreach (var entity in _dbSet.Where(x => x.Rank > oldRank))
{
if(entity.Rank > newRank)
entity.Rank++;
else
entity.Rank--;
}
} else
{
// think the logic yourself.
}
row.Rank = newRank;
如果为行编制索引。排名,可能会相当不错。您可能还希望确保Row.Rank
具有唯一的约束,或者您应该考虑事务。
如果您使用的是大型系统,则可能需要在SQL中实现某种树结构,以便更新元素的秩是O(logn(操作(超快( - 树数据结构的数据库结构
关于实体框架:不,EF 不适用于此类问题。您应该检查此批处理更新/删除EF5,看看是否可以找到有用的内容。事实是,EF 在批量记录中的表现并不好。