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 passons en revue les principes fondamentaux du contrôle d’accès concurrentiel optimiste, puis nous explorons comment l’implémenter à l’aide du contrôle SqlDataSource.

Introduction

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 avions besoin de spécifier l’instruction SQL correspondante INSERTUPDATE, ou DELETE dans les propriétés du contrôle , InsertCommandUpdateCommandou DeleteCommand , ainsi que les paramètres appropriés dans les InsertParameterscollections , UpdateParameterset DeleteParameters . Bien que ces propriétés et collections puissent être spécifiées manuellement, le bouton Avancé de l’Assistant Configuration de la source de données offre une case à cocher Générer INSERT, UPDATEet DELETE des instructions qui créent automatiquement ces instructions en fonction de l’instruction SELECT .

Avec 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’elles sont activées, les WHERE clauses dans les instructions générées UPDATE automatiquement et DELETE sont modifiées pour effectuer la mise à jour ou la suppression uniquement si les données de base de données sous-jacentes n’ont pas été modifiées depuis le dernier chargement des données dans la grille par l’utilisateur.

Vous pouvez ajouter la prise en charge de l’accès concurrentiel optimiste à partir de la boîte de dialogue Options de génération SQL avancées

Figure 1 : Vous pouvez ajouter la prise en charge de l’accès concurrentiel optimiste à partir de la boîte de dialogue Options de génération SQL avancées

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 apporter des retouches sur les principes fondamentaux du contrôle d’accès concurrentiel optimiste, puis découvrir comment l’implémenter à l’aide de SqlDataSource.

Récapitulatif de l’accès concurrentiel 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 remplace accidentellement les modifications d’un autre utilisateur. Dans le didacticiel Implémentation de l’accès concurrentiel optimiste , j’ai fourni l’exemple suivant :

Imaginez que deux utilisateurs, Jisun et Sam, visitaient 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 environ au même moment. Jisun remplace le nom du produit par Chai Tea et clique sur le bouton Mettre à jour. Le résultat net est une UPDATE instruction qui est envoyée à la base de données, qui définit tous les champs de produit pouvant être mis à jour (même si Jisun n’a mis à jour qu’un seul champ, ProductName). À ce stade, la base de données contient les valeurs Chai Tea, la catégorie Boissons, le fournisseur Liquides exotiques, et ainsi de suite pour ce produit particulier. Toutefois, l’écran GridView sur Sam affiche toujours le nom du produit dans la ligne GridView modifiable en tant que Chai. Quelques secondes après la validation des modifications de Jisun, Sam met à jour la catégorie en Condiments et clique sur Mettre à jour. Il en résulte une UPDATE instruction envoyée à la base de données qui définit le nom du produit sur Chai, sur CategoryID l’ID de catégorie Condiments correspondant, etc. Les modifications apportées par Jisun au nom du produit ont été remplacées.

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

Figure 2 : Lorsque deux utilisateurs mettent simultanément à jour un enregistrement, il est possible qu’un utilisateur modifie les autres (cliquez pour afficher l’image en taille réelle)

Pour empêcher ce scénario de se dérouler, une forme de contrôle d’accès concurrentiel doit être implémentée. Accès concurrentiel optimiste L’objectif de ce didacticiel est de supposer que même s’il peut y avoir des conflits d’accès concurrentiel de temps en temps, la grande majorité du temps de tels conflits ne se produisent pas. Par conséquent, en cas de conflit, 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.

Notes

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 s’assurant que l’enregistrement en cours de mise à jour ou de suppression a 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 de l’enregistrement 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 cliqué 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 l’enregistrement de base de données sous-jacent uniquement 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 (cliquer pour afficher l’image en taille réelle)

Il existe différentes approches pour implémenter l’accès concurrentiel optimiste (voir La logique de mise à jour de l’accès concurrentiel optimiste de Peter A. Bromberg pour un bref aperçu d’un certain nombre d’options). La technique utilisée par sqlDataSource (ainsi que par les ADO.NET datasets typés 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 @ProductName paramètres et @UnitPrice contiennent les nouvelles valeurs entrées par l’utilisateur, tandis que @original_ProductName et @original_UnitPrice contiennent les valeurs qui ont été initialement chargées dans gridView lorsque l’utilisateur a cliqué sur le bouton Modifier :

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 d’accès concurrentiel optimiste avec SqlDataSource est aussi simple que de cocher une case à cocher.

Étape 1 : Création d’un SqlDataSource qui prend en charge l’accès concurrentiel optimiste

Commencez par ouvrir la OptimisticConcurrency.aspx page à partir du SqlDataSource dossier. Faites glisser un contrôle SqlDataSource de la boîte à outils sur le Designer, en lui donnant ID la ProductsDataSourceWithOptimisticConcurrencyvaleur . Cliquez ensuite sur le lien Configurer la source de données à partir de la balise active du contrôle. À partir du premier écran de l’Assistant, choisissez d’utiliser le NORTHWINDConnectionString et cliquez sur Suivant.

Choisir d’utiliser le NORTHWINDConnectionString

Figure 4 : Choisir d’utiliser le (Cliquer pour afficher l’imageNORTHWINDConnectionString en taille réelle)

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 le Products tableau dans la liste déroulante et sélectionnez les ProductIDcolonnes , ProductName, UnitPriceet Discontinued , comme illustré dans la figure 5.

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

Figure 5 : À partir de la Products table, renvoyer les ProductIDcolonnes , ProductName, UnitPriceet Discontinued (cliquez pour afficher l’image en taille réelle)

Après avoir sélectionné 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 INSERTles instructions , UPDATEet DELETE et et Utiliser l’accès concurrentiel optimiste, puis cliquez sur OK (reportez-vous à la figure 1 pour obtenir une capture d’écran). Terminez l’Assistant en cliquant sur Suivant, puis sur Terminer.

Après avoir terminé l’Assistant Configuration de la source de données, prenez un moment pour examiner les propriétés et UpdateCommand résultantesDeleteCommand, ainsi que les DeleteParameters collections et .UpdateParameters Le moyen le 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 la UpdateCommand valeur suivante :

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é et DeleteParameters la DeleteCommand collection doivent 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>

En plus d’augmenter les WHERE clauses des UpdateCommand propriétés et ( DeleteCommand et d’ajouter les paramètres supplémentaires aux collections de paramètres respectives), la sélection de l’option Utiliser l’accès concurrentiel optimiste ajuste deux autres propriétés :

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

Notes

É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 augmenté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 qui contiennent des NULL valeurs. Pour voir pourquoi, considérez nos 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 des NULL valeurs. Si un enregistrement particulier a une NULL valeur pour UnitPrice, la partie clause [UnitPrice] = @original_UnitPrice prend toujours la WHERE valeur False, car NULL = NULL retourne toujours False. Par conséquent, les enregistrements qui contiennent des NULL valeurs ne peuvent pas être modifiés ou supprimés, car les UPDATE clauses d’instructions WHERE et DELETE ne retournent aucune ligne à mettre à jour ou à supprimer.

Notes

Ce bogue a été signalé pour la première fois à Microsoft en juin 2004 dans SqlDataSource Génère des instructions SQL incorrectes et devrait ê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 UpdateCommand propriétés et DeleteCommand pour toutes les colonnes qui peuvent avoir des NULL valeurs. En général, remplacez par [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 l’Fenêtre Propriétés, ou via les onglets UPDATE et DELETE de 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 de la UpdateCommand clause et DeleteCommand s WHERE qui peut contenir des NULL valeurs.

L’application de ceci à notre exemple entraîne les modifications et DeleteCommand valeurs suivantes 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
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 les options Modifier et Supprimer

Avec SqlDataSource configuré pour prendre en charge l’accès concurrentiel optimiste, il ne reste plus qu’à ajouter un contrôle Web de données à la page qui utilise ce contrôle d’accès concurrentiel. Pour ce tutoriel, nous allons ajouter un GridView qui fournit à la fois des fonctionnalités d’édition et de suppression. Pour ce faire, faites glisser un GridView de la boîte à outils vers le Designer et définissez son ID sur Products. À partir de la balise active gridView, liez-la au ProductsDataSourceWithOptimisticConcurrency contrôle SqlDataSource ajouté à l’étape 1. Enfin, case activée les options Activer la modification et Activer la suppression de la balise active.

Lier gridView à SqlDataSource et activer la modification et la suppression

Figure 6 : Lier gridView à SqlDataSource et Activer la modification et la suppression (cliquer pour afficher l’image en taille réelle)

Après avoir ajouté GridView, configurez son apparence en supprimant BoundField ProductID , en remplaçant la ProductName propriété s de HeaderText BoundField sur Product et en mettant à jour le UnitPrice BoundField afin que sa HeaderText propriété soit simplement Price. Dans l’idéal, nous améliorerons l’interface de modification pour inclure un RequiredFieldValidator pour la ProductName valeur et un CompareValidator pour la UnitPrice valeur (pour nous assurer qu’il s’agit d’une valeur numérique correctement mise en forme). Pour plus d’informations sur la personnalisation de l’interface de modification de données , reportez-vous au tutoriel Personnalisation de l’interface de modification de GridView.

Notes

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 d’accès concurrentiel 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 et cliquez sur Mettre à jour. Le navigateur est publié et gridView revient à son mode de pré-édition, affichant le nouveau nom de produit pour l’enregistrement qui vient d’être modifié.

Dans la deuxième fenêtre de navigateur, modifiez le prix (en conservant le nom du produit comme valeur d’origine), puis cliquez sur Mettre à jour. Lors de la publication, 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 nouveau nom de produit avec l’ancien prix. Les modifications apportées dans la deuxième fenêtre de navigateur ont été perdues. En outre, les modifications ont été perdues assez discrètement, car il n’y avait aucune exception ou message indiquant qu’une violation de la concurrence vient de se produire.

Les modifications de la deuxième fenêtre de navigateur ont été perdues en mode silencieux

Figure 7 : Les modifications apportées à la deuxième fenêtre du navigateur ont été perdues en mode silencieux (cliquer pour afficher l’image en taille réelle)

La raison pour laquelle les modifications apportées au deuxième navigateur n’ont pas été validées est que la clause s de l’instruction UPDATEWHERE a filtré tous les enregistrements et n’a donc pas affecté les 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 de 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 (puisqu’il a été modifié par le premier navigateur). Par conséquent, l’instruction [ProductName] = @original_ProductName retourne False et n’affecte UPDATE aucun enregistrement.

Notes

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

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. Cependant, il n’y a aucun commentaire visuel 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 l’informons et, peut-être, gardons la grille en mode édition. Voyons comment y parvenir.

Étape 3 : Déterminer quand une violation d’accès concurrentiel s’est produite

Étant donné qu’une violation d’accès concurrentiel rejette les modifications apportées, il serait agréable d’avertir l’utilisateur lorsqu’une violation d’accès concurrentiel s’est produite. Pour alerter l’utilisateur, ajoutons un contrôle Label Web 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 qui a été mis à jour simultanément par un autre utilisateur. Passez en revue les modifications apportées par l’autre utilisateur, puis rétablissez votre mise à jour ou suppression. Définissez la propriété s du CssClass contrôle Label sur Warning, qui est une classe CSS définie dans Styles.css qui affiche le texte dans une police rouge, italique, en gras et grande. Enfin, définissez les propriétés Label s Visible et EnableViewState sur False. Cela masque l’étiquette, à l’exception uniquement des publications pour lesquelles nous définissons explicitement sa Visible propriété 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 (cliquer pour afficher l’image en taille réelle)

Lors d’une mise à jour ou d’une suppression, les gestionnaires d’événements GridView se RowUpdatedRowDeleted déclenchent après que son contrôle de source de données a effectué la mise à jour ou la suppression demandée. Nous pouvons déterminer le nombre de lignes affectées par l’opération à partir de 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 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 case activée la e.AffectedRows propriété et, si elle est égale à 0, définissez la ConcurrencyViolationMessage propriété Label s Visible sur True. Dans le RowUpdated gestionnaire d’événements, nous demandons également à GridView de rester en mode édition en définissant la KeepInEditMode propriété sur true. Ce faisant, nous devons lier 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 s DataBind() GridView.

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 dans le visage d’une violation d’accès concurrentiel

Figure 9 : Un message s’affiche dans la face d’une violation d’accès concurrentiel (cliquer pour afficher l’image en taille réelle)

Résumé

Lors de la création d’une application web dans laquelle plusieurs utilisateurs simultanés peuvent modifier les mêmes données, il est important de prendre en compte les options de contrôle de concurrence. Par défaut, les contrôles web ASP.NET données 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 majeure partie du travail pour l’ajout de clauses augmentées WHERE aux instructions générées UPDATE automatiquement et DELETE , mais il existe quelques subtilités dans la gestion des NULL colonnes de valeur, comme indiqué dans la section Gestion correcte des NULL valeurs.

Ce tutoriel conclut notre examen de SqlDataSource. Nos tutoriels restants retourneront à l’utilisation des données à l’aide de l’objectDataSource et de l’architecture hiérarchisée.

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 comme consultant indépendant, formateur et écrivain. Son dernier livre est Sams Teach Yourself ASP.NET 2.0 in 24 Heures. Il est accessible à l’adressemitchell@4GuysFromRolla.com . ou via son blog, qui peut être trouvé à l’adresse http://ScottOnWriting.NET.