2012-03-06 59 views
1

我有以下实体框架的逻辑,我想转换为存储过程,我一直在使用存储过程中的排它锁尝试,但它会导致很多超时的...翻译下面的C#同步逻辑存储过程

页面视为某种其中有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 

我对存储过程不太了解,但我需要在锁定模式下分配页面,以便不会填充页面。

AM我在想什么? 即使我在交易中运行,我还需要锁定吗?

回答

2

是的,你是过度的。让SQL Server管理锁。

create procedure [GetPageID] 
    @SpaceRequested int 
as 
begin 
    set nocount on; 

    begin tran; 

    update top (1) pages 
    set 
    SpaceAvailable -= @SpaceRequested, 
    SpaceOccupied += @SpaceRequested 
    output 
    inserted.PageID 
    where 
    SpaceAvailable > @SpaceRequested 
    order by PageID asc; 

    commit tran; 

end; 

以上也可以写成你在你的问题出两步的方式(如果你喜欢它,或者你的SQL Server版本是足够老):

create procedure [GetPageID] 
    @SpaceRequested int 
as 
begin 
    set nocount on; 

    begin tran; 

    declare @page_id int; 

    select top (1) @page_id = PageID 
    from pages with (updlock, rowlock) 
    where SpaceAvailable > @SpaceRequested 
    order by PageID asc; 

    update Pages 
    set 
    SpaceAvailable = SpaceAvailable - @SpaceRequested, 
    SpaceOccupied = SpaceOccupied + @SpaceRequested 
    where 
    PageID = @page_id; 

    commit tran; 

    select @page_id; 

end; 
+0

感谢您的回答,但为什么您修改了我当前的逻辑,您认为这是错误的?我不是SP的专家,所以我只是出于好奇而问,看起来你在做一个语句的选择和更新? – 2012-03-06 15:49:56

+0

我没有修改逻辑,我修改了实现。你正在做很多不必要的事情,这些都被删除了。唯一的选择来自'@ res'表,更新语句不会选择任何内容,尽管它返回页面ID。现在我写了这个,我意识到我可以简单地输出到没有临时表的客户端。 – GSerg 2012-03-06 15:53:18

+0

这是否也意味着我不需要存储过程,如果我在TransactionScope中封装相同的逻辑? – 2012-03-07 08:44:23