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.