2013-03-05 3 views
2

J'ai actuellement une méthode qui lit les données pour déterminer si une mise à jour est nécessaire, puis pousse la mise à jour à la base de données (dépendance injectée). La méthode est frappée très fort, et j'ai trouvé des bogues liés à la concurrence, à savoir, plusieurs mises à jour puisque plusieurs threads lisent les données avant la première mise à jour. J'ai résolu cela en utilisant une serrure, ça marche plutôt bien. Comment puis-je utiliser un TransactionScope pour faire la même chose? Puis-je? Va-t-il bloquer un autre thread comme le ferait un verrou? De plus, puis-je 'verrouiller' un 'id' spécifique comme je le fais avec un verrou (je garde un dictionnaire qui stocke un objet à verrouiller pour chaque identifiant)? J'utilise Entity Framework 5, bien qu'il soit masqué par un référentiel et un modèle d'unité de travail.TransactionScope Au lieu d'utiliser un verrou

+1

Qu'en est-il d'un ReadWriterLockSlim à la place? Si vous avez plusieurs lectures et peu d'écritures c'est beaucoup mieux qu'un verrou. (comment TransactionScope rendrait-il thread-safe?) –

+0

Ce serait mieux que juste le verrou (+1). Mais, il ne me permettrait pas de travailler en toute sécurité sur les données ailleurs, ou de travailler à partir d'une autre machine sur les mêmes données. – ccook

+1

ReaderWriterLockSlim est généralement recommandé pour être utilisé _without_ accès récursif autorisé, et permet d'écrire du code encore problématique si cela est permis, une situation périlleuse. Tout aussi périlleux, sinon plus, que celui qu'il a résolu. –

Répondre

2

Le verrouillage du niveau d'application peut ne pas être une solution à ce problème. Tout d'abord, vous devez généralement verrouiller uniquement un enregistrement unique ou une plage d'enregistrements. Ensuite, vous devrez peut-être verrouiller d'autres modifications et entrer dans un code assez complexe.

Cette situation est généralement gérée avec une concurrence optimiste ou pessimiste. Concurrence optimiste - vous aurez une colonne additionnelle générée par la base de données (la base de données a généralement un type spécial comme timestamp ou rowversion). La base de données mettra automatiquement à jour cette colonne chaque fois que vous mettez à jour l'enregistrement. Si vous configurez cette colonne en tant que version de ligne EF inclura la colonne dans la condition where de la mise à jour => la mise à jour exécutée recherchera l'enregistrement avec la clé et la version de ligne données. Si l'enregistrement est trouvé, il sera mis à jour. Si l'enregistrement n'est pas trouvé cela signifie que l'enregistrement avec la clé n'existe pas ou quelqu'un d'autre a mis à jour l'enregistrement puisque le processus courant a chargé ses données => vous obtiendrez une exception et vous pouvez essayer d'actualiser les données et sauvegarder les modifications. Ce mode est utile pour les enregistrements qui ne sont pas trop mis à jour. Dans votre cas, cela peut causer juste un autre problème.

  • Concurrence pessimiste - ce mode utilise plutôt le verrouillage de base de données. Lorsque vous interrogez l'enregistrement, vous le verrouillez pour la mise à jour afin que personne d'autre ne puisse le verrouiller pour mettre à jour ou mettre à jour directement. Malheureusement, ce mode n'a actuellement pas de support direct dans EF et vous devez l'exécuter via SQL brut. J'ai écrit un article explaining the pessimistic concurrency et son utilisation avec EF. Même la concurrence pessimiste peut ne pas être une bonne solution pour la base de données en cas de charge importante. Si vous construisez vraiment une solution où de nombreux processus simultanés tentent de mettre à jour les mêmes données tout le temps, vous pouvez vous retrouver avec une nouvelle conception car il n'y aura pas de solution fiable basée sur le verrouillage ou la relance des mises à jour échouées.

  • +0

    Merci pour les commentaires! Ce que j'ai déployé pour le moment est une combinaison d'un verrou au niveau de l'application (basé sur les performances, si un TryEnter échoue ignore l'action), dans lequel existe une portée d'application avec isolationlevel.repeatableread pour gérer le cas rare d'une concurrence issue de l'extérieur de la méthode. Une mauvaise solution? – ccook