【问题标题】:Lock table for read锁定表以供读取
【发布时间】:2017-09-22 10:28:25
【问题描述】:

我们有一个电子商务网站,人们可以在其中购买礼品卡。这些卡片存储在一个表中,并按整数排序以用于逻辑目的。如果有人购买了礼品卡,我们会在数据库中插入采购订单并编辑第一张符合条件的卡的字段,以便知道它不再可用。

但有时当 2 位用户同时购买一张卡时,两人都会获得同一张卡。所以我们与IsolationLevel.ReadCommitted 进行了交易,但它不起作用。对于 4 次并发点击,有 1 个相同的 Card 对应 2 个 PurchaseOrder,另一个 Card 对应 2 个 PurchaseOrder

TransactionOptions transOption = new TransactionOptions();
transOption.IsolationLevel = IsolationLevel.ReadCommitted; 

using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, transOption))
{
    PurchaseOrder purchaseOrder = GetFull(request.OrderId);
    purchaseOrder.ChangeTracker.ChangeTrackingEnabled = true;
    purchaseOrder.IdStatus = DbConstants.RefPurchaseOrderStatus.Completed;

    //select the quantity of purchased gift cards and update them with ObjectContext.SaveChanges()
    List<Card> cards = _cardBusiness.AssignCards(purchaseOrder.Quantity, purchaseOrder.IdProduct, purchaseOrder.IdUserAccount, purchaseOrder.IdChannel);

    Card[] arrCards = cards.ToArray();

    int count = 0;
    //Set a link between a PurchaseOrder Line and the Card
    foreach(PurchaseOrderLine line in purchaseOrder.PurchaseOrderLines)
    {
        line.ChangeTracker.ChangeTrackingEnabled = true;
        line.IdCard = arrCards[count].Id;
        count++;
    }

    PurchaseOrder po = Update(purchaseOrder);

    scope.Complete(); 

    return true;
}

快速评论:在交易中,我们得到之前插入的PurchaseOrder,更改其状态,然后选择可用的礼品卡,编辑一些属性,SaveChanges()并返回礼品卡链接到PurchaseOrderLine

但我们显然没有锁定卡片选择。我们怎么能这样做?

更新:

public static List<Card> AssignCards(int qty, int idProduct, Guid idUserAccount, int? idChannel)
{
    using (RestopolitanEntities context = GetContext())
    {
        try
        {
            var Xcards = (from c in context.Cards
                         select c);

            List<Card> cards = Xcards.OrderBy(c => c.IdOrder).Take(qty).ToList();

            foreach (Card card in cards)
            {
                card.ChangeTracker.ChangeTrackingEnabled = true;
                card.UseDate = DateTime.Now;
            }

            context.SaveChanges();

            return cards.ToList();
        }
        catch (Exception ex)
        {
            HandleException(ex);
        }
    }
    return null;
}

这次我尝试了IsolationLevel.RepeatableRead,一个Card不再归属于2个PurchaseOrder。但是AssignCards() 没有返回Card。对于 4 次并发点击,有 2 个 PurchaseOrder 有 1 个不同的 Card 和 2 个 PurchaseOrder 没有 Card

【问题讨论】:

  • AssignCards() 是做什么的?可以加一下这个方法的代码吗?
  • 也许您可以使用数据库序列创建解决方案,因为数据库会注意您不会获得已使用的数字...
  • 您使用的是什么数据库平台?
  • SQL Server 2014 实体框架 6

标签: c# sql entity-framework locking transactionscope


【解决方案1】:

要么在事务开始时获取应用锁,

context.Database.ExecuteSqlCommand("exec sp_getapplock 'AssignCards','Exclusive';");

或者在标识要使用的卡片的查询中使用一些锁定提示:

var sql = "select top (@qty) CardId from Cards with (rowlock, updlock, readpast) where UseDate is null";
var cardIds = context.Database.SqlQuery<int>(sql,qty).ToList();

List<Card> cards = context.Cards.Where(c => cardIds.Contains(c.CardId)).ToList();

如果您使用锁定提示,您可以使用 READPAST 提示允许并发会话跳过被其他会话锁定的卡片。如果您使用应用锁,您可以在交易过程中解除锁(选择卡片后)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-16
    • 1970-01-01
    相关资源
    最近更新 更多