Comment implémenter un mécanisme de locking simple pour une application multi-user?

Je ne veux vraiment pas réinventer le puits ici, donc je request des idées pour implémenter un mécanisme de locking simple (ligne) dans une application DB multi-user.

Supposons que j'ai une table appelée Products qui a bien sûr un ID (PK), et aussi une colonne rowversion (qui n'est pas utilisée jusqu'ici), et je veux permettre à un seul user d'éditer une ligne spécifique.

Pendant que les users modifient l'logging (après un "logging"), les autres users peuvent uniquement afficher cet logging (en lecture seule), mais pas le modifier ou le supprimer. lorsque l'user a terminé et sauvegardé l'logging ("check out"), cet logging sera de nouveau disponible pour être édité / supprimé par d'autres users.

J'ai quelques idées (comme append une colonne "status", ou peut-être créer une table "lock"), mais il y a aussi des considérations si "l'user bloquant" tient l'logging depuis longtime (supposons qu'il soit parti pour un week-end et a laissé son ordinateur ouvert en mode édition). aussi comment débloquer l'logging si le programme s'est écrasé / mise hors tension du système sur la machine client …

Je me demandais s'il y avait un bon et relativement simple model pour cela (qui pourrait inclure des fonctionnalités SQL-Server)?

BTW, mon application client est Delphi / ADO (pas que ce soit très pertinent).

Une solution simple que j'ai implémentée dans une application ….

 CREATE TABLE RecordLocks( [RecordId] [varchar](8) NOT NULL, [UserName] [varchar](100) NOT NULL, [datetimestamp] [smalldatetime] NOT NULL, [PC] [varchar](100) NOT NULL ) GO 

datetimestamp a une valeur par défaut de GetDate() RecordId est un VARCHAR raison de la key primaire dans la table que je verrouille (pas mon choix). Aussi cette table a les index évidents

 CREATE PROCEDURE usp_LockRecord @RecordId VARCHAR(8), @UserName VARCHAR(100), @ComputerName VARCHAR(100) AS BEGIN BEGIN TRAN; DELETE FROM RecordLocks WHERE DATEDIFF(HOUR, datetimestamp, GETDATE()) > 2; IF NOT EXISTS (Select * from RecordLocks WHERE RecordId = @RecordId) INSERT INTO RecordLocks (RecordId, username, PC) VALUES (@RecordId, @UserName, @ComputerName); Select * from RecordLocks WHERE RecordId = @RecordId; COMMIT TRAN; END GO 

Supprimez d'abord et enregistrez plus de 2 heures (changez pour convenir)

Vérifiez qu'il n'y a pas d'logging qui verrouille déjà celui à verrouiller et si ce n'est pas le cas, insérez le verrou.

Sélectionnez l'logging avec le RecordId qui nous intéresse.

Ensuite, dans le code d'appel, vérifiez si le locking a réussi. Si le nom d'user et le PC revenant des sélections correspondent, datatables qui viennent d'être transmises dans le verrou ont réussi. Si le nom d'user correspond mais que le PC n'a pas le même user, l'logging est ouvert sur une autre machine. Si le nom d'user ne correspond pas, un autre user l'a déjà ouvert. J'affiche un message à l'user si son IE échoué Cet logging est actuellement bloqué par JoeB sur le post de travail XYZ.

Lorsque l'user enregistre l'logging ou navigue, il suffit de supprimer le locking d'logging.

Je suis sûr qu'il existe d'autres façons, mais cela fonctionne bien pour moi.

Mettre à jour

Un logging ne sera inséré que s'il n'existe pas. La sélection suivante va returnner un logging. Si le nom d'user et / ou le PC est différent des données, vous essayez d'insert l'logging est déjà verrouillé par un autre user (ou le même user sur une machine différente). Donc, un appel fait tout (pour ainsi dire). Donc, si je fais un appel Exec usp_LockRecord(1234, 'JoeB', 'Workstation1') et l'logging que je reçois correspond à ces données, j'ai réussi à get un verrou sur cet logging. Si le nom d'user et / ou PC que je reçois est différent, l'logging est déjà verrouillé. Je peux ensuite afficher un message indiquant à l'user que l'logging est verrouillé, rendre les champs en lecture seule, désactiver les buttons de sauvegarde et leur dire qui a un verrou dessus si je le souhaite.

Avec un horodatage, vous pouvez sortir avec "sortingcher". Le stream de travail ressemble à ceci:

  1. Lire la ligne (y compris l'horodatage) dans la memory
  2. Laissez l'user faire quelques modifications, en suivant les anciennes et les nouvelles valeurs
  3. Résultats user "save"
  4. Lire à nouveau l'horodatage de la ligne (dans un niveau d'isolement qui empêche les lectures non répétables)
    1. Si l'horodatage récemment lu est le même que l'ancien horodatage, mettez à jour la ligne avec les valeurs modifiées par l'user et validez
    2. Si l'horodatage récemment lu est différent de l'ancien horodatage, vous pouvez tenter de "merge" les modifications (c'est-à-dire si un set de propriétés complètement disjoint a changé, les deux modifications sont compatibles). S'il y a chevauchement, avertir l'user et interdire la sauvegarde

Ça devrait le faire.