Partager via


Implémentation de l’accès concurrentiel optimiste avec SqlDataSource (VB)

par Scott Mitchell

Télécharger le PDF

Dans ce tutoriel, nous allons examiner les éléments essentiels du contrôle d’accès concurrentiel optimiste, puis explorer comment l’implémenter à l’aide du contrôle SqlDataSource.

Présentation

Dans le tutoriel précédent, nous avons examiné comment ajouter des fonctionnalités d’insertion, de mise à jour et de suppression au contrôle SqlDataSource. En bref, pour fournir ces fonctionnalités, nous devions spécifier l'instruction SQL correspondante INSERT, UPDATE, ou DELETE dans les propriétés de contrôle InsertCommand, UpdateCommand, ou DeleteCommand, ainsi que les paramètres appropriés dans les collections InsertParameters, UpdateParameters, et DeleteParameters. Bien que ces propriétés et collections puissent être spécifiées manuellement, le bouton Configurer la source de données avancé offre une case à cocher Générer INSERT, UPDATEet DELETE des instructions qui créent automatiquement ces instructions en fonction de l’instruction SELECT .

En plus de la case à cocher Générer INSERT, UPDATEet DELETE les instructions, la boîte de dialogue Options de génération SQL avancées inclut une option Utiliser l’accès concurrentiel optimiste (voir la figure 1). Lorsqu'ils sont cochés, les clauses WHERE des instructions UPDATE et DELETE générées automatiquement sont modifiées pour uniquement effectuer la mise à jour ou la suppression si les données sous-jacentes de la base de données n'ont pas été modifiées depuis que l'utilisateur a importé les données dans la grille pour la dernière fois.

Vous pouvez ajouter la prise en charge de la concurrence optimiste à partir de la boîte de dialogue Options avancées de génération SQL

Figure 1 : Vous pouvez ajouter la prise en charge de la concurrence optimiste depuis la boîte de dialogue Options avancées de génération SQL.

De retour dans le didacticiel Implémentation de l’accès concurrentiel optimiste , nous avons examiné les principes fondamentaux du contrôle d’accès concurrentiel optimiste et comment l’ajouter à ObjectDataSource. Dans ce tutoriel, nous allons retoucher les éléments essentiels du contrôle d’accès concurrentiel optimiste, puis explorer comment l’implémenter à l’aide de SqlDataSource.

Récapitulatif de la concurrence optimiste

Pour les applications web qui permettent à plusieurs utilisateurs simultanés de modifier ou de supprimer les mêmes données, il existe une possibilité qu’un utilisateur puisse remplacer accidentellement les modifications d’un autre utilisateur. Dans le tutoriel sur l'implémentation de la concurrence optimiste, j’ai fourni l’exemple suivant :

Imaginez que deux utilisateurs, Jisun et Sam, visitaient toutes deux une page dans une application qui permettait aux visiteurs de mettre à jour et de supprimer des produits via un contrôle GridView. Les deux cliquez sur le bouton Modifier pour Chai en même temps. Jisun remplace le nom du produit par Chai Tea et clique sur le bouton Mettre à jour. Le résultat net est une UPDATE instruction envoyée à la base de données, qui définit tous les champs pouvant être mis à jour du produit (même si Jisun n’a mis à jour qu’un seul champ, ProductName). À ce stade, la base de données a les valeurs Chai Tea, la catégorie Boissons, le fournisseur Liquides exotiques, et ainsi de suite pour ce produit particulier. Toutefois, la GridView sur l'écran de Sam affiche toujours le nom du produit dans la ligne modifiable de la GridView sous forme de Chai. Quelques secondes après la validation des modifications de Jisun, Sam met à jour la catégorie sur Condiments et clique sur Mettre à jour. Cela entraîne l’envoi d’une UPDATE instruction à la base de données qui définit le nom du produit sur Chai, l’ID CategoryID de catégorie Condiments correspondant, et ainsi de suite. Les modifications apportées au nom du produit ont été remplacées par Jisun.

La figure 2 illustre cette interaction.

Lorsque deux utilisateurs mettent simultanément à jour un enregistrement, il est possible qu’un utilisateur change pour remplacer l’autre s

Figure 2 : Lorsque deux utilisateurs mettent simultanément à jour un enregistrement, il est possible que les modifications d’un utilisateur remplacent les autres (cliquez pour afficher l’image de taille complète)

Pour empêcher le déploiement de ce scénario, une forme de contrôle d’accès concurrentiel doit être implémentée. L’accès concurrentiel optimiste abordé dans ce didacticiel se base sur l’hypothèse qu’il peut y avoir des conflits d’accès concurrentiel de temps à autre, mais que la grande majorité du temps, de tels conflits ne surviendront pas. Par conséquent, si un conflit se produit, le contrôle d’accès concurrentiel optimiste informe simplement l’utilisateur que ses modifications ne peuvent pas être enregistrées, car un autre utilisateur a modifié les mêmes données.

Remarque

Pour les applications où il est supposé qu’il y aura de nombreux conflits d’accès concurrentiel ou si ces conflits ne sont pas tolérables, le contrôle d’accès concurrentiel pessimiste peut être utilisé à la place. Reportez-vous au didacticiel Implémentation de l’accès concurrentiel optimiste pour une discussion plus approfondie sur le contrôle d’accès concurrentiel pessimiste.

Le contrôle d’accès concurrentiel optimiste fonctionne en veillant à ce que l’enregistrement mis à jour ou supprimé ait les mêmes valeurs que lors du démarrage du processus de mise à jour ou de suppression. Par exemple, lorsque vous cliquez sur le bouton Modifier dans un GridView modifiable, les valeurs des enregistrements sont lues à partir de la base de données et affichées dans TextBoxes et d’autres contrôles Web. Ces valeurs d’origine sont enregistrées par GridView. Plus tard, une fois que l’utilisateur a apporté ses modifications et clique sur le bouton Mettre à jour, l’instruction UPDATE utilisée doit prendre en compte les valeurs d’origine plus les nouvelles valeurs et mettre à jour uniquement l’enregistrement de base de données sous-jacent si les valeurs d’origine que l’utilisateur a commencé à modifier sont identiques aux valeurs toujours dans la base de données. La figure 3 illustre cette séquence d’événements.

Pour que la mise à jour ou la suppression réussisse, les valeurs d’origine doivent être égales aux valeurs de base de données actuelles

Figure 3 : Pour que la mise à jour ou la suppression réussisse, les valeurs d’origine doivent être égales aux valeurs de base de données actuelles (cliquez pour afficher l’image de taille complète)

Il existe différentes approches pour implémenter l’accès concurrentiel optimiste (consultez la logique de mise à jour d’accès concurrentiel optimiste de Peter A. Bromberg pour un bref aperçu de plusieurs options). La technique utilisée par SqlDataSource (ainsi que par les jeux de données typés ADO.NET utilisés dans notre couche d’accès aux données) augmente la WHERE clause pour inclure une comparaison de toutes les valeurs d’origine. L’instruction suivante UPDATE , par exemple, met à jour le nom et le prix d’un produit uniquement si les valeurs de base de données actuelles sont égales aux valeurs qui ont été récupérées à l’origine lors de la mise à jour de l’enregistrement dans GridView. Les paramètres @ProductName et @UnitPrice contiennent les nouvelles valeurs entrées par l’utilisateur, tandis que les paramètres @original_ProductName et @original_UnitPrice contiennent les valeurs qui ont été initialement chargées dans le GridView lorsque le bouton Modifier a été cliqué :

UPDATE Products SET
    ProductName = @ProductName,
    UnitPrice = @UnitPrice
WHERE
    ProductID = @original_ProductID AND
    ProductName = @original_ProductName AND
    UnitPrice = @original_UnitPrice

Comme nous le verrons dans ce tutoriel, l’activation du contrôle de concurrence optimiste avec le SqlDataSource est aussi simple que de cocher une case.

Étape 1 : Création d'un SqlDataSource prenant en charge la concurrence optimiste

Commencez par ouvrir la OptimisticConcurrency.aspx page à partir du SqlDataSource dossier. Faites glisser un contrôle SqlDataSource à partir de la boîte à outils vers le Concepteur, puis positionnez sa ID propriété sur ProductsDataSourceWithOptimisticConcurrency. Cliquez ensuite sur le lien Configurer la source de données à partir de la balise intelligente du contrôle. Dans le premier écran de l'Assistant, choisissez de travailler avec le NORTHWINDConnectionString et cliquez sur Suivant.

Choisir d’utiliser NORTHWINDConnectionString

Figure 4 : Choisir d’utiliser l’image NORTHWINDConnectionString (cliquez pour afficher l’image de taille complète)

Pour cet exemple, nous allons ajouter un GridView qui permet aux utilisateurs de modifier la Products table. Par conséquent, dans l’écran Configurer l’instruction Select, choisissez la table Products dans la liste déroulante, puis sélectionnez les colonnes ProductID, ProductName, UnitPrice, et Discontinued, comme illustré dans la figure 5.

Dans la table Products, retournez les colonnes ProductID, ProductName, UnitPrice et Discontinued

Figure 5 : À partir du Products tableau, renvoyez les colonnes ProductID, ProductName, UnitPrice et Discontinued (cliquez pour afficher l’image en taille réelle)

Après avoir choisi les colonnes, cliquez sur le bouton Avancé pour afficher la boîte de dialogue Options avancées de génération SQL. Cochez les cases Générer les instructions INSERT, UPDATE et DELETE, Utiliser les cases à cocher de concurrence optimiste, puis cliquez sur OK (reportez-vous à la figure 1 pour obtenir une capture d’écran). Terminez l’Assistant en cliquant sur Suivant, puis Terminez.

Une fois l’Assistant Configuration de la source de données terminé, prenez un moment pour examiner les propriétés DeleteCommand et UpdateCommand résultantes et les collections DeleteParameters et UpdateParameters. La méthode la plus simple consiste à cliquer sur l’onglet Source dans le coin inférieur gauche pour afficher la syntaxe déclarative de la page. Vous y trouverez une UpdateCommand valeur de :

UPDATE [Products] SET
     [ProductName] = @ProductName,
     [UnitPrice] = @UnitPrice,
     [Discontinued] = @Discontinued
WHERE
     [ProductID] = @original_ProductID AND
     [ProductName] = @original_ProductName AND
     [UnitPrice] = @original_UnitPrice AND
     [Discontinued] = @original_Discontinued

Avec sept paramètres dans la UpdateParameters collection :

<asp:SqlDataSource ID="ProductsDataSourceWithOptimisticConcurrency"
    runat="server" ...>
    <DeleteParameters>
      ...
    </DeleteParameters>
    <UpdateParameters>
        <asp:Parameter Name="ProductName" Type="String" />
        <asp:Parameter Name="UnitPrice" Type="Decimal" />
        <asp:Parameter Name="Discontinued" Type="Boolean" />
        <asp:Parameter Name="original_ProductID" Type="Int32" />
        <asp:Parameter Name="original_ProductName" Type="String" />
        <asp:Parameter Name="original_UnitPrice" Type="Decimal" />
        <asp:Parameter Name="original_Discontinued" Type="Boolean" />
    </UpdateParameters>
    ...
</asp:SqlDataSource>

De même, la propriété DeleteCommand et la collection DeleteParameters devraient ressembler à ce qui suit :

DELETE FROM [Products]
WHERE
     [ProductID] = @original_ProductID AND
     [ProductName] = @original_ProductName AND
     [UnitPrice] = @original_UnitPrice AND
     [Discontinued] = @original_Discontinued
<asp:SqlDataSource ID="ProductsDataSourceWithOptimisticConcurrency"
    runat="server" ...>
    <DeleteParameters>
        <asp:Parameter Name="original_ProductID" Type="Int32" />
        <asp:Parameter Name="original_ProductName" Type="String" />
        <asp:Parameter Name="original_UnitPrice" Type="Decimal" />
        <asp:Parameter Name="original_Discontinued" Type="Boolean" />
    </DeleteParameters>
    <UpdateParameters>
        ...
    </UpdateParameters>
    ...
</asp:SqlDataSource>

Outre l'augmentation des clauses des propriétés WHERE et UpdateCommand (ainsi que l'ajout de paramètres supplémentaires aux collections de paramètres respectives), l'option Utiliser l'accès concurrent optimiste ajuste deux autres propriétés :

Lorsque le contrôle Web de données appelle sqlDataSource s Update() ou Delete() méthode, il transmet les valeurs d’origine. Si la propriété SqlDataSource ConflictDetection est définie sur CompareAllValues, ces valeurs d’origine sont ajoutées à la commande. La OldValuesParameterFormatString propriété fournit le modèle d’affectation de noms utilisé pour ces paramètres de valeur d’origine. L'Assistant de configuration de la source de données utilise original_{0} et nomme chaque paramètre d'origine dans les propriétés UpdateCommand et DeleteCommand et les collections UpdateParameters et DeleteParameters en conséquence.

Remarque

Étant donné que nous n’utilisons pas les fonctionnalités d’insertion du contrôle SqlDataSource, n’hésitez pas à supprimer la InsertCommand propriété et sa InsertParameters collection.

Gestion correcte desNULLvaleurs

Malheureusement, les instructions améliorées UPDATE et DELETE générées automatiquement par l’Assistant Configurer la source de données lors de l’utilisation de l’accès concurrentiel optimiste ne fonctionnent pas avec les enregistrements contenant des NULL valeurs. Pour voir pourquoi, tenez compte de notre SqlDataSource s UpdateCommand:

UPDATE [Products] SET
     [ProductName] = @ProductName,
     [UnitPrice] = @UnitPrice,
     [Discontinued] = @Discontinued
WHERE
     [ProductID] = @original_ProductID AND
     [ProductName] = @original_ProductName AND
     [UnitPrice] = @original_UnitPrice AND
     [Discontinued] = @original_Discontinued

La UnitPrice colonne de la Products table peut avoir NULL valeurs. Si un enregistrement particulier a une NULL valeur pour UnitPrice, la WHERE partie [UnitPrice] = @original_UnitPrice clause prend toujours la valeur False, car NULL = NULL elle retourne toujours False. Par conséquent, les enregistrements qui contiennent des valeurs NULL ne peuvent pas être modifiés ou supprimés, car les instructions UPDATE et clauses DELETEWHERE ne retournent aucune ligne à mettre à jour ou à supprimer.

Remarque

Ce bogue a été signalé pour la première fois à Microsoft en juin 2004 dans SqlDataSource génère des instructions SQL incorrectes et est censé être corrigé dans la prochaine version de ASP.NET.

Pour résoudre ce problème, nous devons mettre à jour manuellement les WHERE clauses dans les propriétés UpdateCommand et DeleteCommand de toutes les colonnes qui peuvent avoir des valeurs NULL. En général, passez [ColumnName] = @original_ColumnName à :

(
   ([ColumnName] IS NULL AND @original_ColumnName IS NULL)
     OR
   ([ColumnName] = @original_ColumnName)
)

Cette modification peut être effectuée directement via le balisage déclaratif, via les options UpdateQuery ou DeleteQuery de la fenêtre Propriétés, ou via les onglets UPDATE et DELETE dans l’option Spécifier une instruction SQL personnalisée ou une procédure stockée dans l’Assistant Configurer la source de données. Là encore, cette modification doit être effectuée pour chaque colonne dans UpdateCommand, DeleteCommand et WHERE qui peuvent contenir des valeurs NULL.

L’application de cet exemple entraîne la modification des valeurs suivantes pour UpdateCommand et DeleteCommand :

UPDATE [Products] SET
     [ProductName] = @ProductName,
     [UnitPrice] = @UnitPrice,
     [Discontinued] = @Discontinued
WHERE
     [ProductID] = @original_ProductID AND
     [ProductName] = @original_ProductName AND
     (([UnitPrice] IS NULL AND @original_UnitPrice IS NULL)
        OR ([UnitPrice] = @original_UnitPrice)) AND
     [Discontinued] = @original_Discontinued
DELETE FROM [Products]
WHERE
     [ProductID] = @original_ProductID AND
     [ProductName] = @original_ProductName AND
     (([UnitPrice] IS NULL AND @original_UnitPrice IS NULL)
        OR ([UnitPrice] = @original_UnitPrice)) AND
     [Discontinued] = @original_Discontinued

Étape 2 : Ajout d’un GridView avec des options d’édition et de suppression

Avec SqlDataSource configuré pour prendre en charge la concurrence optimiste, il ne reste plus qu'à ajouter à la page un contrôle Web de données qui utilise ce contrôle de concurrence optimiste. Pour ce tutoriel, nous allons ajouter un GridView qui fournit à la fois des fonctionnalités de modification et de suppression. Pour ce faire, faites glisser un GridView à partir de la boîte à outils sur le Concepteur et définissez ses ID sur Products. À partir de la balise intelligente GridView, liez-la au contrôle SqlDataSource ajouté à l'étape 1 ProductsDataSourceWithOptimisticConcurrency. Enfin, cochez les options Activer la modification et Activer la suppression dans la balise intelligente.

Lier GridView à SqlDataSource et activer la modification et la suppression

Figure 6 : Lier GridView à SqlDataSource et activer la modification et la suppression (cliquez pour afficher l’image de taille complète)

Après avoir ajouté GridView, configurez son apparence en supprimant ProductID BoundField, en modifiant la ProductName propriété BoundField en HeaderText Product et en mettant à jour UnitPrice BoundField afin que sa HeaderText propriété soit simplement Price. Dans l’idéal, nous allons améliorer l’interface d’édition pour inclure un RequiredFieldValidator pour la ProductName valeur et un CompareValidator pour la UnitPrice valeur (pour vous assurer qu’il s’agit d’une valeur numérique correctement mise en forme). Reportez-vous au didacticiel Personnalisation de l’interface de modification de données pour une présentation plus approfondie de la personnalisation de l’interface d’édition de GridView.

Remarque

L’état d’affichage de GridView doit être activé, car les valeurs d’origine passées de GridView à SqlDataSource sont stockées dans l’état d’affichage.

Après avoir apporté ces modifications à GridView, le balisage déclaratif GridView et SqlDataSource doit ressembler à ce qui suit :

<asp:SqlDataSource ID="ProductsDataSourceWithOptimisticConcurrency"
    runat="server" ConflictDetection="CompareAllValues"
    ConnectionString="<%$ ConnectionStrings:NORTHWNDConnectionString %>"
    DeleteCommand=
        "DELETE FROM [Products]
         WHERE [ProductID] = @original_ProductID
         AND [ProductName] = @original_ProductName
         AND (([UnitPrice] IS NULL AND @original_UnitPrice IS NULL)
              OR ([UnitPrice] = @original_UnitPrice))
         AND [Discontinued] = @original_Discontinued"
    OldValuesParameterFormatString=
        "original_{0}"
    SelectCommand=
        "SELECT [ProductID], [ProductName], [UnitPrice], [Discontinued]
         FROM [Products]"
    UpdateCommand=
        "UPDATE [Products]
         SET [ProductName] = @ProductName, [UnitPrice] = @UnitPrice,
            [Discontinued] = @Discontinued
         WHERE [ProductID] = @original_ProductID
         AND [ProductName] = @original_ProductName
         AND (([UnitPrice] IS NULL AND @original_UnitPrice IS NULL)
            OR ([UnitPrice] = @original_UnitPrice))
        AND [Discontinued] = @original_Discontinued">
    <DeleteParameters>
        <asp:Parameter Name="original_ProductID" Type="Int32" />
        <asp:Parameter Name="original_ProductName" Type="String" />
        <asp:Parameter Name="original_UnitPrice" Type="Decimal" />
        <asp:Parameter Name="original_Discontinued" Type="Boolean" />
    </DeleteParameters>
    <UpdateParameters>
        <asp:Parameter Name="ProductName" Type="String" />
        <asp:Parameter Name="UnitPrice" Type="Decimal" />
        <asp:Parameter Name="Discontinued" Type="Boolean" />
        <asp:Parameter Name="original_ProductID" Type="Int32" />
        <asp:Parameter Name="original_ProductName" Type="String" />
        <asp:Parameter Name="original_UnitPrice" Type="Decimal" />
        <asp:Parameter Name="original_Discontinued" Type="Boolean" />
    </UpdateParameters>
</asp:SqlDataSource>
<asp:GridView ID="Products" runat="server"
    AutoGenerateColumns="False" DataKeyNames="ProductID"
    DataSourceID="ProductsDataSourceWithOptimisticConcurrency">
    <Columns>
        <asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
        <asp:BoundField DataField="ProductName" HeaderText="Product"
            SortExpression="ProductName" />
        <asp:BoundField DataField="UnitPrice" HeaderText="Price"
            SortExpression="UnitPrice" />
        <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
            SortExpression="Discontinued" />
    </Columns>
</asp:GridView>

Pour voir le contrôle de concurrence optimiste en action, ouvrez deux fenêtres de navigateur et chargez la OptimisticConcurrency.aspx page dans les deux. Cliquez sur les boutons Modifier pour le premier produit dans les deux navigateurs. Dans un navigateur, modifiez le nom du produit, puis cliquez sur Mettre à jour. Le navigateur effectuera un retour et GridView reviendra à son mode d'avant modification, affichant le nouveau nom du produit pour l'enregistrement qui vient d'être modifié.

Dans la deuxième fenêtre du navigateur, modifiez le prix (mais laissez le nom du produit comme valeur d’origine), puis cliquez sur Mettre à jour. Lors du re-postage, la grille revient à son mode de pré-édition, mais la modification du prix n’est pas enregistrée. Le deuxième navigateur affiche la même valeur que le premier : le nom du nouveau produit avec l'ancien prix. Les modifications apportées dans la deuxième fenêtre de navigateur ont été perdues. De plus, les modifications ont été perdues assez silencieusement, car il n’y avait aucune exception ni message indiquant qu’une violation de concurrence s’est produite.

Les modifications apportées à la deuxième fenêtre du navigateur ont été perdues en mode silencieux

Figure 7 : Les modifications apportées à la deuxième fenêtre du navigateur ont été perdues silencieusement (cliquez pour afficher l’image de taille complète)

La raison pour laquelle les modifications du deuxième navigateur n’ont pas été validées était parce que la UPDATE clause s WHERE de l’instruction a filtré tous les enregistrements et n’a donc pas affecté de lignes. Examinons à nouveau l’instruction UPDATE :

UPDATE [Products] SET
     [ProductName] = @ProductName,
     [UnitPrice] = @UnitPrice,
     [Discontinued] = @Discontinued
WHERE
     [ProductID] = @original_ProductID AND
     [ProductName] = @original_ProductName AND
     (([UnitPrice] IS NULL AND @original_UnitPrice IS NULL) OR
        ([UnitPrice] = @original_UnitPrice)) AND
     [Discontinued] = @original_Discontinued

Lorsque la deuxième fenêtre du navigateur met à jour l’enregistrement, le nom de produit d’origine spécifié dans la WHERE clause ne correspond pas au nom de produit existant (car il a été modifié par le premier navigateur). Par conséquent, l’instruction [ProductName] = @original_ProductName retourne False et n’affecte UPDATE aucun enregistrement.

Remarque

Supprimer fonctionne de la même manière. Avec deux fenêtres de navigateur ouvertes, commencez par modifier un produit donné avec un, puis enregistrez ses modifications. Après avoir enregistré les modifications dans le navigateur, cliquez sur le bouton Supprimer pour le même produit dans l’autre. Étant donné que les valeurs d'origine ne correspondent pas dans la clause DELETE de l'instruction WHERE, la suppression échoue silencieusement.

Du point de vue de l’utilisateur final dans la deuxième fenêtre du navigateur, après avoir cliqué sur le bouton Mettre à jour, la grille revient au mode de pré-édition, mais ses modifications ont été perdues. Toutefois, il n’y a pas de commentaires visuels que leurs modifications n’ont pas collé. Dans l’idéal, si les modifications d’un utilisateur sont perdues en cas de violation d’accès concurrentiel, nous les informons et, peut-être, maintenons la grille en mode édition. Examinons comment procéder.

Étape 3 : Déterminer quand une violation de concurrence s’est produite

Étant donné qu’une violation d’accès concurrentiel rejette les modifications qu’il a apportées, il serait agréable d’alerter l’utilisateur lorsqu’une violation d’accès concurrentiel s’est produite. Pour alerter l’utilisateur, nous allons ajouter un contrôle Web Label en haut de la page nommée ConcurrencyViolationMessage dont Text la propriété affiche le message suivant : Vous avez tenté de mettre à jour ou de supprimer un enregistrement mis à jour simultanément par un autre utilisateur. Passez en revue les modifications de l'autre utilisateur, puis refaites votre mise à jour ou supprimez-la. Définissez la propriété du contrôle Label CssClass sur Warning, qui est une classe CSS définie dans Styles.css, et qui affiche le texte en rouge, en italique, en gras, et en grande police. Enfin, définissez les propriétés Visible et EnableViewState des étiquettes sur False. Cela masquera l'étiquette, à l'exception seulement de ceux des retours où nous définissons explicitement sa propriété Visible sur True.

Ajouter un contrôle d’étiquette à la page pour afficher l’avertissement

Figure 8 : Ajouter un contrôle d’étiquette à la page pour afficher l’avertissement (cliquez pour afficher l’image de taille complète)

Lors de l’exécution d’une mise à jour ou d’une suppression, les gestionnaires d’événements RowUpdated et RowDeleted sont déclenchés après que le contrôle de source de données a exécuté la mise à jour ou la suppression demandée. Nous pouvons déterminer le nombre de lignes affectées par l’opération grâce à ces gestionnaires d’événements. Si aucune ligne n’a été affectée, nous voulons afficher l’étiquette ConcurrencyViolationMessage .

Créez un gestionnaire d’événements pour les RowUpdated événements et RowDeleted ajoutez le code suivant :

Protected Sub Products_RowUpdated(sender As Object, e As GridViewUpdatedEventArgs) _
    Handles Products.RowUpdated
    If e.AffectedRows = 0 Then
        ConcurrencyViolationMessage.Visible = True
        e.KeepInEditMode = True
        ' Rebind the data to the GridView to show the latest changes
        Products.DataBind()
    End If
End Sub
Protected Sub Products_RowDeleted(sender As Object, e As GridViewDeletedEventArgs) _
    Handles Products.RowDeleted
    If e.AffectedRows = 0 Then
        ConcurrencyViolationMessage.Visible = True
    End If
End Sub

Dans les deux gestionnaires d'événements, nous vérifions la propriété e.AffectedRows, et si elle est égale à 0, nous définissons la propriété ConcurrencyViolationMessage du Label Visible sur True. Dans le RowUpdated gestionnaire d’événements, nous demandons également à GridView de rester en mode d’édition en définissant la KeepInEditMode propriété sur true. Dans ce cas, nous devons rebiner les données à la grille afin que les données de l’autre utilisateur soient chargées dans l’interface d’édition. Pour ce faire, appelez la méthode GridView DataBind() .

Comme le montre la figure 9, avec ces deux gestionnaires d’événements, un message très visible s’affiche chaque fois qu’une violation d’accès concurrentiel se produit.

Un message s’affiche en réponse à une violation de concurrence

Figure 9 : Un message s’affiche en face d’une violation d’accès concurrentiel (cliquez pour afficher l’image de taille complète)

Résumé

Lors de la création d’une application web où plusieurs utilisateurs simultanés peuvent modifier les mêmes données, il est important de prendre en compte les options de contrôle d’accès concurrentiel. Par défaut, les contrôles web de données ASP.NET et les contrôles de source de données n’utilisent aucun contrôle d’accès concurrentiel. Comme nous l’avons vu dans ce tutoriel, l’implémentation d’un contrôle d’accès concurrentiel optimiste avec SqlDataSource est relativement rapide et facile. SqlDataSource gère la plupart des étapes de l'ajout de clauses augmentées WHERE aux instructions UPDATE et DELETE générées automatiquement, mais il existe quelques subtilités dans la gestion des colonnes de valeurs NULL, comme indiqué dans la section Gestion correcte des valeurs NULL.

Ce tutoriel conclut notre examen de SqlDataSource. Nos tutoriels restants reviennent à travailler avec les données en utilisant ObjectDataSource et une architecture à plusieurs niveaux.

Bonne programmation !

À propos de l’auteur

Scott Mitchell, auteur de sept livres ASP/ASP.NET et fondateur de 4GuysFromRolla.com, travaille avec les technologies Web Microsoft depuis 1998. Scott travaille en tant que consultant indépendant, formateur et écrivain. Son dernier livre est Sams Teach Yourself ASP.NET 2.0 en 24 heures. On peut le joindre à mitchell@4GuysFromRolla.com.