2010-05-24 5 views
5

Je travaille sur une application de commande sandwich dans ASP.NET MVC, C# et LINQ to SQL. L'application tourne autour de l'utilisateur créant plusieurs sandwichs faits sur mesure à partir d'une sélection d'ingrédients. Quand il s'agit de confirmer la commande, j'ai besoin de savoir qu'il y a assez de portions de chaque ingrédient pour remplir tous les sandwiches dans l'ordre de l'utilisateur avant de m'engager dans la DB, car il est possible qu'un ingrédient soit épuisé. en l'ajoutant à leur panier et en confirmant la commande.LINQ to SQL: Problème de concurrence

Un peu sur la base de données:

Ingrédients - Détails ingrédient de magasins, y compris nombre de portions
ordre - Table d'en-tête pour un ordre, ne fait qu'enregistrer le temps de commande
OrderDetail - Enregistre une enregistrement de chaque sandwich dans une commande
OrderDetailItem - Enregistre chaque ingrédient dans chaque sandwich dans une commande

Donc, fondamentalement, je me demande quelle est la meilleure approche pour m'assurer qu'avant d'ajouter des enregistrements à Order, OrderDetail et OrderDetailItem, je peux m'assurer que la commande peut être satisfaite.

+0

Vous avez une condition de concurrence inhérente, toute solution est sur les compromis possibles que différentes approches vous donnent. – Richard

Répondre

2
try 
    { 
     Begin netTransaction(); 
     If (IsEnoughIngredients()) 
     { 
      1. Build your sandwich 
      2. Add sandwich to data context with a timestamp (or whatever you chose for concurrency checking) 
      3. SubmitChangesToDataContext() 
      4. CommitNetTransaction() 
     } 
    } catch (Exception e) 
    { 
     Rollback netTransaction(); 
     InformUser(); 
    } 
+1

ou utilisez un TransactionScope – RobS

+0

vikp - comment implémenteriez-vous le contrôle de concurrence à l'étape 2? – Gib

+0

En outre, je vous recommande d'être prudent avec votre sélection de niveau d'isolation de transaction. – RobS

0

Vous devez mettre à jour la quantité Ingredient chaque fois que vous enregistrez un sandwich. Cependant, cela empêchera les autres utilisateurs d'utiliser le même ingrédient avant de valider vos modifications (même si le stock est suffisant).

Il est préférable d'utiliser une table de transfert qui s'engagerait après chaque ingrédient ajouté. Cela rendrait les changements visibles à la fois.

Lorsque vous êtes prêt à soumettre votre commande dans son ensemble, les enregistrements sont simplement déplacés de la table de transfert vers la table permanente.

Vous devez cependant implémenter un mécanisme ROLLBACK vous-même pour gérer les enregistrements obsolètes. Par exemple, un travail cron qui permettrait de surveiller l'activité sur les commandes et supprimer ceux qui n'ont pas été actifs pendant 10 minutes ou plus.

1

Nombre d'approches que vous pourriez prendre ici mais je ferais quelque chose comme le pseudo code suivant. Hypothèse sûre que généralement il y aura suffisamment d'ingrédients pour satisfaire la commande, structurez ainsi le contrôle des transactions autour de cette hypothèse et traitez les rares exceptions.

Begin transaction (Isolation = Repeatable Read) 

For Each OrderDetail In Order.OrderDetailCollection 
    For Each OrderDetailItem In OrderDetail.OrderDetailItemCollection 
     Update Ingredient 
     Set Portions = (Portions – OrderDetailItem.Portions) 
     Where Ingredient.ID = OrderDetailItem.IngredientID 
     And (Portions – OrderDetailItems.Portions) >= 0 

     If RecordsAffected != 1 Then 
      Rollback Transaction 
      SufficientStock = false 
      Exit For 
     End If 
    Next 

    If(SufficientStock = false) 
     Exit For 
    End If 
Next 

Edit: Si vous pouvez être amenés à éloigner de linqing tout, une approche alternative pour éviter le round tripping serait quelque chose le long des lignes ci-dessous:

Begin transaction 
    Insert Order (return OrderID) 
    Insert OrderDetails 
    Insert OrderDetailItems 

    Execute update stock stored procedure (see below) 
    If (Success) 
     Commit transaction 
    Else 
     Rollback transaction 
    End IF 

Code pour le niveau des stocks de mise à jour procédure:

CREATE PROCEDURE dbo.StockLevel_UpdateByOrderID 
(
    @OrderID INT 
    , @Success BIT 
) 

SET NOCOUNT ON 
SET TRANSACTION ISOLATION LEVEL REPEATEABLE READ 

BEGIN TRANSACTION 

DECLARE @IngredientCount INT 

-- Determine number of ingredients in whole order 
SELECT 
    @IngredientCount = COUNT(odi.IngredientID) 
FROM 
    dbo.OrderDetailItem odi 
INNER JOIN 
    dbo.OrderDetail od 
ON od.OrderDetailID = odi.OrderDetailID 
WHERE 
    od.OrderID = 1 
GROUP BY 
    odi.IngredientID  

-- Update stock levels for all ingredients 
UPDATE 
    dbo.Ingredient 
SET 
    Portions = (i.Portions - odi.TotalPortions) 
FROM 
    dbo.Ingredient i 
INNER JOIN 
    (
    SELECT 
     odi.IngredientID 
     , SUM(odi.Portions) AS TotalPortions 
    FROM 
     dbo.OrderDetailItem odi 
    INNER JOIN 
     dbo.OrderDetail od 
    ON od.OrderDetailID = odi.OrderDetailID 
    WHERE 
     od.OrderID = 1 
    GROUP BY 
     odi.IngredientID 
    ) odi 
ON odi.IngredientID = i.IngredientID 
WHERE 
    (i.Portions - odi.TotalPortions) >= 0 

-- Is number of ingredients updated correct? 
IF(@@ROWCOUNT != @IngredientCount) 
BEGIN 
    ROLLBACK TRANSACTION 
    SET @Success = 0 
END 
ELSE 
BEGIN 
    COMMIT TRANSACTION 
    SET @Success = 0 
END 
0

Code de DB est bon à savoir mais, pas bon d'être lié à un db si vous pouvez l'aider. L'affichage du menu doit être basé sur les ingrédients actuels au moment de l'affichage, , donc être en dehors des ingrédients devrait être une exception rare ou votre sandwicherie ne durera pas assez longtemps pour se soucier de la qualité du logiciel. Comment éviter les allers-retours pour la mise à jour des ingrédients? Le lien prend-il en charge une sorte d'insertion de lots dans la table des ingrédients? cifey