2017-09-22 4 views
0

Nous avons un site Web de commerce électronique où les gens peuvent acheter une carte-cadeau. Ces cartes sont stockées dans une table et commandées par un nombre entier à des fins logistiques. Si quelqu'un achète une carte-cadeau, nous insérons un bon de commande dans la base de données et modifions un champ de la première carte éligible afin de savoir qu'elle n'est plus disponible.Verrouiller la table pour lire

Mais parfois, lorsque 2 utilisateurs achètent une carte en même temps, la même carte est donnée pour les deux. Nous avons donc fait une transaction avec IsolationLevel.ReadCommitted qui ne fonctionne pas. Pour 4 clics simultanés, il y a 1 même Card pour 2 PurchaseOrder et un autre Card pour 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; 
} 

commentaire rapide: Dans la transaction, nous obtenons les inséré précédemment PurchaseOrder, modifier son état, puis sélectionnez la carte-cadeau disponible (s), modifier certaines propriétés, SaveChanges() et retourner la carte-cadeau (s) pour relier au PurchaseOrderLine. Mais nous ne verrons évidemment pas la sélection de la carte. Comment pourrions-nous faire cela?

Mise à jour:

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; 
} 

Cette fois j'ai essayé avec IsolationLevel.RepeatableRead, un Card n'est plus attribué à 2 PurchaseOrder. Mais aucun Card sont retournés de AssignCards(). Pour 4 clics simultanés, il y a 2 PurchaseOrder avec 1 distinct Card et 2 PurchaseOrder sans Card.

+0

Que fait AssignCards()? Pouvez-vous ajouter du code de cette méthode? – MikkaRin

+1

Peut-être que vous pouvez créer une solution avec une séquence de base de données, car la base de données prendra soin de ne pas avoir un numéro déjà utilisé ... – DatRid

+0

Quelle plate-forme de base de données utilisez-vous? – mjwills

Répondre

1

Soit un verrou d'application au début de la transaction,

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

Ou utiliser quelques conseils de verrouillage sur la requête qui identifie les cartes à utiliser:

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

Si vous utilisez le verrou Vous pouvez utiliser l'indicateur READPAST pour autoriser les sessions simultanées à ignorer les cartes verrouillées par d'autres sessions. Et si vous utilisez le verrou d'application, vous pouvez libérer le verrou au milieu de la transaction (après avoir sélectionné les cartes).