Mise en évidence du comportement des évènements de table d'Access 2010 en environnement multi-utilisateurs

Christophe WARIN

Ce court article met en évidence le comportement des évènements de table au sein d'un environnement multi-utilisateurs. Il est destiné à un public averti maîtrisant les concepts d'évènements de table.

- Titre : Mise en évidence du comportement des évènements de table d'Access 2010 en environnement multi-utilisateurs

- Auteur : Christophe WARIN

- Parution : 10 novembre 2009

- Licence : Copyright ® 2009 Christophe Warin. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Mise en situation

- III. Banc d'essai

- IV. Conclusion

I - Introduction

Dans un précédent document, je vous proposais de comprendre le fonctionnement des évènements de table au sein d'une transaction de données. Cette fois-ci, je vous propose de nous intéresser à leur comportement dans un environnement multi-utilisateurs et plus précisément de déterminer comment se traduisent, dans l'application d'utilisateur B, les modifications apportées par un utilisateur A.

II - Mise en situation

Soit une table tblClient possédant la structure suivante :

- ID (Clé primaire)

- Nom

- Prénom

- Matricule

Le matricule d'un client est composé des deux premières lettres de son nom, suivies des 2 premières de son prénom. Un indice vient compléter la donnée pour éviter les doublons :

ID

Nom

Prenom

Matricule

1

NORD

Paul

NOPa1

2

NORD

Pauline

NOPa2

Celui-ci est généré par la DataMacro suivante placée dans l'évènement AfterInsert() de la table tblClient

Look Up A Record In qryMatricule
Where  qryMatricule.N=left(TblClient.Nom,2) AND qryMatricule.P=left(tblClient.Prenom,2)
Alias rec_Matricule

SetLocalVar(vMatricule,[NB])

Edit Record
Set Field(Matricule,Left(Nom,2) & left(Prenom,2) & VMatricule
End Edit Record

Code de la requête qryMatricule :

SELECT COUNT(ID) AS NB, Left(Nom,2) AS N, Left(Prenom,3) AS P
FROM TBLCLIENT
GROUP BY Left(Nom,2), Left(Prenom,3);

L'étude du contenu de la table tblClient montre bien que le calcul est élaboré après l'insertion de l'enregistrement. En effet, si ce n'était pas le cas, le matricule de Paul Nord se terminerait par 0 (Count(ID) d'une table vide est égal à 0). On en conclut donc, que pour l'utilisateur ayant opéré la saisie, l'évènement AfterInsert intervient (comme son nom l'indique) après la création de la nouvelle ligne par le moteur de base de données. Mais qu'en est-il pour les autres utilisateurs ? Ont-ils la même vision de la table tblClient ? C'est justement à cette question que le prochain chapitre va tenter de répondre.

III - Banc d'essai

Il existe deux hypothèses possibles concernant le contenu que voit l'utilisateur B lorsque la saisie de A est traitée par l'évènement AfterInsert :

- Soit B voit l'enregistrement en cours de création, mais avec le champ Matricule non renseigné (car non calculé), auquel cas nous pourrions parler d'état incohérent de la base.

- Soit B ne voit pas ce nouvel enregistrement qui ne lui sera disponible qu'une fois tous les évènements de table terminés.

Pour mettre en évidence ces hypothèses, nous allons modifier la DataMacro de telle sorte qu'elle nous laisse le temps de rafraîchir l'affichage du contenu de la base de l'utilisateur B. La solution la plus simple et la plus efficace consiste à créer une fonction VBA nommée Attendre dans un module et de la lancer via la méthode SetLocalVar.

Fonction Attendre :

Function Attendre()
While True
DoEvents
Wend
End Function

DataMacro AfterInsert :

SetLocalVar(vTemp;Attendre())
Look Up A Record In qryMatricule
Where  qryMatricule.N=left(TblClient.Nom,2) AND qryMatricule.P=left(tblClient.Prenom,2)
Alias rec_Matricule

SetLocalVar(vMatricule,[NB])

Edit Record
Set Field(Matricule,Left(Nom,2) & left(Prenom,2) & VMatricule
End Edit Record

L'utilisateur A travaille sur la base de données dorsale tandis que B est connecté à celle-ci via le mécanisme de table liée.

Avant le test, nous pouvons constater que A et B disposent du même contenu de la table tblClient :

ID

Nom

Prenom

Matricule

1

NORD

Paul

NOPa1

2

NORD

Pauline

NOPa2

Tentons une insertion d'un nouveau client depuis l'application de l'utilisateur A.

ID

Nom

Prenom

Matricule

1

NORD

Paul

NOPa1

2

NORD

Pauline

NOPa2

3

NORD

Polux

Comme convenu, l'application Access de A ne rend pas la main puisque occupée par la boucle infinie de la fonction Attendre(). On en déduit au passage que les évènements de table ne sont pas asynchrones avec Access. Il est nécessaire de terminer toutes les macros pour être autorisé à poursuivre. A ce stade, voici l'état de la table clients de l'utilisateur B après rafraîchissement :

ID

Nom

Prenom

Matricule

1

NORD

Paul

NOPa1

2

NORD

Pauline

NOPa2

L'enregistrement actuellement affecté par l'évènement AfterInsert n'est pas visible.

Un rapide retour dans l'éditeur VB de A permet de stopper la fonction en remplaçant la constante True par False. Instantanément, le champ Matricule est mis à jour et affiché sur l'écran de A :

ID

Nom

Prenom

Matricule

1

NORD

Paul

NOPa1

2

NORD

Pauline

NOPa2

3

NORD

Polux

NOPo1

Simultanément, un rafraîchissement de la table de l'utilisateur B montre les mêmes données.

ID

Nom

Prenom

Matricule

1

NORD

Paul

NOPa1

2

NORD

Pauline

NOPa2

3

NORD

Polux

NOPo1

IV - Conclusion

Ce rapide test permet de nous assurer qu'à aucun moment la base n'apparaît dans un état incohérent à un quelconque utilisateur. L'exemple est ici borné à l'évènement AfterInsert mais il faut savoir qu'il en va de même pour les autres évènements After*.

Si cette conclusion n'exprime pas grand-chose pour vous, il faut en fait penser à l'ensemble des situations où les évènements de table peuvent être utilisés et y voir les avantages et les inconvénients. Dans le cas ci-dessus, B est ravi d'apprendre qu'il ne listera pas des clients non finalisés, en revanche, s'il lance la création de NORD Polette alors que A est ralenti pour X raisons dans son évènement de table, il est fort probable qu'ils s'allouent tous les deux le même matricule pour, hélas, deux clients différents. Il est donc primordial que les DataMacros soient optimisées au maximum de telle sorte à minimiser ces risques.