2010-08-09 9 views
2

J'ai une table de base de données et une classe d'entité correspondante (POCO avec proxy de suivi des changements) avec un membre qui sert de compteur:Lecture, incrémentation et réécriture d'un compteur unique pour plusieurs utilisateurs (dans EF 4)

Il y a une fonction dans mon application web qui doit récupérer cette entité par ID, lire Counter (qui est ensuite utilisée pour être assignée à d'autres objets), l'incrémenter et réécrire l'entité dans la base de données, comme :

public int GetNewCounter(int ID) 
{ 
    int newCounter = 0; 
    using (MyObjectContext ctx = new MyObjectContext()) 
    { 
     MyEntity ent = ctx.MyEntities.Where(e => e.ID == ID).Single(); 
     ++ent.Counter; 
     newCounter = ent.Counter; 
     ctx.SaveChanges(); 
    } 
    return newCounter; 
} 
// newCounter is now used for other stuff 

Deux utilisateurs ou plus pourraient faites ceci en même temps (avec l'entité du même ID) et je dois m'assurer qu'ils ne lisent pas et n'utilisent pas le même compteur (ainsi le deuxième utilisateur ne devrait pas pouvoir acquérir un compteur avant que le premier utilisateur n'ait stocké le compteur incrémenté dans la base de données).

Est-il possible de "verrouiller" une Entité avant qu'elle ne soit lue et de libérer le verrou après avoir réécrit le changement de compteur et ensuite vérifier un verrou lorsqu'un autre utilisateur essaie de lire la même Entité? Est-ce que c'est un modèle raisonnable?

Quelles options ai-je pour implémenter cette exigence avec Entity Framework (EF 4)?

Je suis très familier avec les caractéristiques et les idées de concurrence dans SQL Server et Entity Framework et apprécierait d'obtenir de l'aide dans la bonne direction avec ce problème spécifique.

Merci d'avance!

Répondre

1

Je suis assez sûr que TransactionScope (à l'aide d'une transaction) verrouille la table, mais vous pouvez être sûr que ce que vous voulez (un verrou de table). Vous pouvez également regarder ConcurrencyMode = "fixed", même si je ne suis pas sûr que cela vous donnera exactement ce que vous cherchez.

+0

Merci pour votre réponse! Je vais regarder de plus près les deux fonctionnalités demain. À mon avis, je ne pas besoin d'un « verrou de table », mais plutôt un verrou sur « niveau ligne » ou simple « niveau d'objet », mais je ne sais pas si une telle chose existe. – Slauma

0

J'ai essayé une solution à l'aide optimiste et concurrency suivant la réponse de ThatSteveGuy en réglant le ConcurrencyMode-fixed pour la propriété Counter dans MyEntity dans le fichier modèle edmx.

Je serais heureux de voir si quelqu'un pourrait examiner ce que je ne sais pas si c'est une bonne solution ou s'il y a un moyen plus facile.

public int GetNewCounter(int ID) 
{ 
    int newCounter = 0; 
    using (MyObjectContext ctx = new MyObjectContext()) 
    { 
     MyEntity ent = ctx.MyEntities.Where(e => e.ID == ID).Single(); 
     int iRetries = 0; 
     bool success = false; 

     do 
     { 
      try 
      { 
       ++ent.Counter; 
       newCounter = ent.Counter; 
       ctx.SaveChanges(); 
       success = true; 
      } 
      catch (OptimisticConcurrencyException) 
      { 
       ctx.Refresh(RefreshMode.StoreWins, ent); 
       ++iRetries; 
       if (iRetries == 3) // give up after 3 retries 
        throw; 
      } 
     } while (!success); 
    } 
    return newCounter; 
} 
Questions connexes