C# 将以下C同步逻辑转换为存储过程

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();

我有以下实体框架逻辑,我想转换为存储过程,我曾尝试在存储过程中使用排他锁,但它会导致大量超时

把页面想象成有4列的硬盘

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在获取数据供您稍后比较/修改时不会设置更新锁。有一个,也就是说,它将在更新时检查行自读取操作以来是否没有更改,并且只有在没有更改时才进行更新,但这样,您必须多次重新运行读取/更新循环,直到成功,而存储的过程将第一次成功。