C# 将以下C同步逻辑转换为存储过程
我有以下实体框架逻辑,我想转换为存储过程,我曾尝试在存储过程中使用排他锁,但它会导致大量超时 把页面想象成有4列的硬盘C# 将以下C同步逻辑转换为存储过程,c#,tsql,stored-procedures,locking,C#,Tsql,Stored Procedures,Locking,我有以下实体框架逻辑,我想转换为存储过程,我曾尝试在存储过程中使用排他锁,但它会导致大量超时 把页面想象成有4列的硬盘 Pages PageID SpaceAvailable SpaceOccupied TotalSpace 我需要在页面中分配我的对象,因为空间是可用的,如果对象不适合,它将获得下一个可用页面 // a static lock to prevent race condition static object Locker = new Object();
Pages
PageID
SpaceAvailable
SpaceOccupied
TotalSpace
我需要在页面中分配我的对象,因为空间是可用的,如果对象不适合,它将获得下一个可用页面
// a static lock to prevent race condition
static object Locker = new Object();
long AllocateNewPage(MyContext context, int requestedSize){
long pageID = 0;
// what is T-SQL lock equaivalent?
lock(Locker){
using(TransactionScope scope = new TransactionScope()){
var page = context.Pages
.Where(x=>x.SpaceAvailable>requestedSize)
.OrderBy(x=>x.PageID)
.First();
page.SpaceOccupied = page.SpaceOccupied + requestedSize;
page.SpaceAvailable = page.SpaceAvailable - requestedSize;
context.SaveChanges();
scope.Commit();
pageID = page.PageID;
}
}
return pageID;
}
下面是我编写的存储过程,但它会导致大量超时,因为我设置了5秒的超时时间,否则同样的事情在C语言中会正确运行,并且速度相当快,唯一的问题是,我必须将其移动到存储过程,因为数据库现在将服务于多个客户端
CREATE procedure [GetPageID]
(
@SpaceRequested int
)
AS
BEGIN
DECLARE @DBID int
DECLARE @lock int
DECLARE @LockName varchar(20)
SET @LockName = 'PageLock'
BEGIN TRANSACTION
-- acquire a lock
EXEC @lock = sp_getapplock
@Resource = @LockName,
@LockMode = 'Exclusive',
@LockTimeout = 5000
IF @lock<>0 BEGIN
ROLLBACK TRANSACTION
SET @DBID = -1
SELECT @DBID
return 0
END
SET @DBID = coalesce((SELECT TOP 1 PageID
FROM Pages
WHERE SpaceAvailable > @SpaceRequested
ORDER BY PageID ASC ),0)
UPDATE Pages SET
SpaceAvailable = SpaceAvailable - @SpaceRequested,
SpaceOccupied = SpaceOccupied + @SpaceRequested
WHERE PageID = @DBID
EXEC @lock = sp_releaseapplock @Resource = @LockName
COMMIT TRANSACTION
SELECT @DBID
END
我对存储过程了解不多,但我需要在锁定模式下分配页面,这样页面就不会被过度填充
我是不是想得太多了?
即使我在事务中运行,我还需要锁定吗?是的,你想得太多了。让SQL Server管理锁 创建过程[GetPageID] @等距整数 像 开始 不计数; 开始训练; 更新前1页 设置 SpaceAvailable-=@SpaceRequested, SpaceOccessed+=@SpaceRequested 输出 插入的.PageID 哪里 SpaceAvailable>@SpaceRequested 按页面ID asc排序; 提交传输; 终止 如果您愿意,或者您的SQL Server版本足够旧,也可以在问题中以两步方式编写上述内容: 创建过程[GetPageID] @等距整数 像 开始 不计数; 开始训练; 声明@page_id int; 选择顶部1@page_id=PageID 从具有updlock、rowlock的页面 如果可用空间>@SpaceRequested 按页面ID asc排序; 更新页面 设置 SpaceAvailable=SpaceAvailable-@SpaceRequested, SpaceOccessed=SpaceOccessed+@SpaceRequested 哪里 PageID=@page_id; 提交传输; 选择@page\u id; 终止
谢谢你的回答,但你为什么要修改我目前的逻辑,你认为这是不正确的?我不是SP方面的专家,所以我只是出于好奇问一下,看起来您在一条语句中同时执行select和update?我没有修改逻辑,我修改了实现。你正在做很多不必要的事情,这些都被删除了。唯一的选择是从@res table,update语句没有选择任何内容,尽管它返回页面ID。现在我写了这个,我意识到我可以不使用temp表直接输出到客户端。这是否也意味着如果我在TransactionScope中封装相同的逻辑,我就不需要存储过程?@AkashKava No,最好有一个存储过程。LINQ或EF在获取数据供您稍后比较/修改时不会设置更新锁。有一个,也就是说,它将在更新时检查行自读取操作以来是否没有更改,并且只有在没有更改时才进行更新,但这样,您必须多次重新运行读取/更新循环,直到成功,而存储的过程将第一次成功。