Partager via


Gestion des exceptions de niveau BLL et DAL dans une page ASP.NET (VB)

par Scott Mitchell

Télécharger le PDF

Dans ce tutoriel, nous allons voir comment afficher un message d’erreur convivial et informatif en cas d’exception lors d’une opération d’insertion, de mise à jour ou de suppression d’un contrôle web de données ASP.NET.

Introduction

L’utilisation des données d’une application web ASP.NET à l’aide d’une architecture d’application hiérarchisée implique les trois étapes générales suivantes :

  1. Déterminez quelle méthode de la couche logique métier doit être appelée et quelles valeurs de paramètre pour la passer. Les valeurs de paramètre peuvent être codées en dur, affectées par programmation ou entrées entrées par l’utilisateur.
  2. Appelez la méthode.
  3. Traitez les résultats. Lors de l’appel d’une méthode BLL qui retourne des données, cela peut impliquer la liaison des données à un contrôle Web de données. Pour les méthodes BLL qui modifient des données, cela peut inclure l’exécution d’une action basée sur une valeur de retour ou la gestion correcte de toute exception survenue à l’étape 2.

Comme nous l’avons vu dans le tutoriel précédent, les contrôles ObjectDataSource et Data Web fournissent des points d’extensibilité pour les étapes 1 et 3. GridView, par exemple, déclenche son RowUpdating événement avant d’affecter ses valeurs de champ à la collection objectDataSource UpdateParameters ; son RowUpdated événement est déclenché une fois l’opération terminée par ObjectDataSource.

Nous avons déjà examiné les événements qui se déclenchent à l’étape 1 et nous avons vu comment ils peuvent être utilisés pour personnaliser les paramètres d’entrée ou annuler l’opération. Dans ce tutoriel, nous allons porter notre attention sur les événements qui se déclenchent une fois l’opération terminée. Avec ces gestionnaires d’événements post-niveau, nous pouvons, entre autres, déterminer si une exception s’est produite pendant l’opération et la gérer correctement, en affichant un message d’erreur convivial et informatif à l’écran plutôt que d’utiliser par défaut la page d’exception ASP.NET standard.

Pour illustrer l’utilisation de ces événements post-niveau, créons une page qui répertorie les produits dans un GridView modifiable. Lors de la mise à jour d’un produit, si une exception est levée, notre page ASP.NET affiche un court message au-dessus du GridView expliquant qu’un problème s’est produit. C’est parti !

Étape 1 : Création d’un GridView modifiable de produits

Dans le tutoriel précédent, nous avons créé un GridView modifiable avec seulement deux champs, ProductName et UnitPrice. Cela nécessitait la création d’une surcharge supplémentaire pour la méthode de UpdateProduct la ProductsBLL classe, qui n’acceptait que trois paramètres d’entrée (nom du produit, prix unitaire et ID) par opposition à un paramètre pour chaque champ de produit. Pour ce tutoriel, nous allons à nouveau pratiquer cette technique, en créant un GridView modifiable qui affiche le nom du produit, la quantité par unité, le prix unitaire et les unités en stock, mais permet uniquement la modification du nom, du prix unitaire et des unités en stock.

Pour prendre en charge ce scénario, nous aurons besoin d’une autre surcharge de la UpdateProduct méthode, qui accepte quatre paramètres : le nom du produit, le prix unitaire, les unités en stock et l’ID. Ajoutez la méthode suivante à la classe ProductsBLL :

<System.ComponentModel.DataObjectMethodAttribute _
    (System.ComponentModel.DataObjectMethodType.Update, True)> _
Public Function UpdateProduct _
    (ByVal productName As String, ByVal unitPrice As Nullable(Of Decimal), _
ByVal unitsInStock As Nullable(Of Short), ByVal productID As Integer) As Boolean
    Dim products As Northwind.ProductsDataTable = _
        Adapter.GetProductByProductID(productID)
    If products.Count = 0 Then
        Return False
    End If
    Dim product As Northwind.ProductsRow = products(0)
    product.ProductName = productName
    If Not unitPrice.HasValue Then
        product.SetUnitPriceNull()
    Else
        product.UnitPrice = unitPrice.Value
    End If
    If Not unitsInStock.HasValue Then
        product.SetUnitsInStockNull()
    Else
        product.UnitsInStock = unitsInStock.Value
    End If
    Dim rowsAffected As Integer = Adapter.Update(product)
    Return rowsAffected = 1
End Function

Une fois cette méthode terminée, nous sommes prêts à créer la page ASP.NET qui permet de modifier ces quatre champs de produit particuliers. Ouvrez la ErrorHandling.aspx page dans le EditInsertDelete dossier et ajoutez un GridView à la page via le Designer. Liez gridView à un nouvel ObjetDataSource, en mappant la Select() méthode à la méthode de GetProducts() la ProductsBLL classe et la Update() méthode à la UpdateProduct surcharge qui vient de se créer.

Utiliser la méthode UpdateProduct Overload qui accepte quatre paramètres d’entrée

Figure 1 : Utiliser la surcharge de méthode UpdateProduct qui accepte quatre paramètres d’entrée (cliquer pour afficher l’image en taille réelle)

Cela crée un ObjectDataSource avec une UpdateParameters collection avec quatre paramètres et un GridView avec un champ pour chacun des champs de produit. Le balisage déclaratif d’ObjectDataSource affecte à la propriété la OldValuesParameterFormatString valeur original_{0}, ce qui provoquera une exception, car notre classe BLL ne s’attend pas à ce qu’un paramètre d’entrée nommé original_productID soit transmis. N’oubliez pas de supprimer ce paramètre de la syntaxe déclarative (ou définissez-le sur la valeur par défaut, {0}).

Ensuite, analysez le GridView pour inclure uniquement les ProductNamechamps , QuantityPerUnit, UnitPriceet UnitsInStock BoundField. N’hésitez pas non plus à appliquer toute mise en forme au niveau du champ que vous jugez nécessaire (par exemple, la modification des HeaderText propriétés).

Dans le tutoriel précédent, nous avons examiné comment mettre en forme le BoundField en tant que devise à la UnitPrice fois en mode lecture seule et en mode édition. Faisons de même ici. Rappelez-vous que cela nécessitait de définir la propriété de DataFormatString BoundField sur {0:c}, sa HtmlEncode propriété falsesur et sa ApplyFormatInEditModetruesur , comme illustré dans la figure 2.

Configurer unitPrice BoundField pour qu’il s’affiche en tant que devise

Figure 2 : Configurer boundField UnitPrice pour qu’il s’affiche en tant que devise (cliquer pour afficher l’image en taille réelle)

La mise en forme du UnitPrice en tant que devise dans l’interface de modification nécessite la création d’un gestionnaire d’événements pour l’événement RowUpdating GridView qui analyse la chaîne au format monétaire en une decimal valeur. Rappelez-vous que le RowUpdating gestionnaire d’événements du dernier tutoriel a également vérifié que l’utilisateur a fourni une UnitPrice valeur. Toutefois, pour ce tutoriel, nous allons autoriser l’utilisateur à omettre le prix.

Protected Sub GridView1_RowUpdating(ByVal sender As Object, _
    ByVal e As System.Web.UI.WebControls.GridViewUpdateEventArgs) _
    Handles GridView1.RowUpdating
    If e.NewValues("UnitPrice") IsNot Nothing Then
        e.NewValues("UnitPrice") = _
            Decimal.Parse(e.NewValues("UnitPrice").ToString(), _
            System.Globalization.NumberStyles.Currency)
    End If

Notre GridView inclut un QuantityPerUnit objet BoundField, mais ce boundField doit être uniquement à des fins d’affichage et ne doit pas être modifiable par l’utilisateur. Pour organiser cela, définissez simplement la propriété BoundFields ReadOnly sur true.

Rendre l’objet QuantityPerUnit BoundField en lecture seule

Figure 3 : Rendre l' QuantityPerUnit Read-Only BoundField (cliquer pour afficher l’image en taille réelle)

Enfin, case activée la case Activer la modification de la balise active GridView. Une fois ces étapes terminées, le ErrorHandling.aspx Designer de la page doit ressembler à la figure 4.

Supprimez tous les champs limités nécessaires et cochez la case Activer la modification

Figure 4 : Supprimez tous les champs limités nécessaires et cochez la case Activer la modification (cliquez pour afficher l’image en taille réelle)

À ce stade, nous avons une liste de tous les champs , , QuantityPerUnitet des produits ; toutefois, seuls les ProductNamechamps , UnitPriceet peuvent UnitsInStock être modifiés. ProductNameUnitsInStockUnitPrice

Les utilisateurs peuvent désormais facilement modifier les noms, les prix et les unités des produits dans les champs stock

Figure 5 : Les utilisateurs peuvent désormais facilement modifier les noms, les prix et les unités des produits dans les champs stock (cliquez pour afficher l’image en taille réelle)

Étape 2 : Gestion correcte des exceptions DAL-Level

Bien que notre GridView modifiable fonctionne à merveille lorsque les utilisateurs entrent des valeurs légales pour le nom, le prix et les unités du produit modifié en stock, la saisie de valeurs illégales entraîne une exception. Par exemple, l’omission de la valeur entraîne la ProductName levée d’une exception NoNullAllowedException , car la ProductName propriété de la ProductsRow classe a la AllowDBNull propriété définie falsesur ; si la base de données est en panne, un SqlException sera levée par tableAdapter lors de la tentative de connexion à la base de données. Sans aucune action, ces exceptions s’activent de la couche d’accès aux données vers la couche logique métier, puis vers la page ASP.NET et enfin vers le runtime ASP.NET.

Selon la façon dont votre application web est configurée et si vous visitez l’application à partir de localhost, une exception non prise en charge peut entraîner une page d’erreur de serveur générique, un rapport d’erreurs détaillé ou une page web conviviale. Consultez Gestion des erreurs d’application web dans ASP.NET et l’élément customErrors pour plus d’informations sur la façon dont le runtime ASP.NET répond à une exception non interceptée.

La figure 6 montre l’écran rencontré lors de la tentative de mise à jour d’un produit sans spécifier la ProductName valeur. Il s’agit du rapport d’erreurs détaillé par défaut affiché lors de l’exécution de localhost.

Omettre le nom du produit affiche les détails de l’exception

Figure 6 : L’omission du nom du produit affiche les détails de l’exception (cliquez pour afficher l’image en taille réelle)

Bien que ces détails d’exception soient utiles lors du test d’une application, présenter un utilisateur final avec un tel écran face à une exception n’est pas idéal. Un utilisateur final ne sait probablement pas ce qu’est ou NoNullAllowedException pourquoi il a été provoqué. Une meilleure approche consiste à présenter à l’utilisateur un message plus convivial expliquant qu’il y a eu des problèmes lors de la tentative de mise à jour du produit.

Si une exception se produit lors de l’exécution de l’opération, les événements post-niveau du contrôle ObjectDataSource et du contrôle Web de données permettent de la détecter et d’annuler l’exception d’un bouillonnement dans le ASP.NET runtime. Pour notre exemple, nous allons créer un gestionnaire d’événements pour l’événement GridView qui détermine si une exception a été déclenchée et, si c’est RowUpdated le cas, affiche les détails de l’exception dans un contrôle Label Web.

Commencez par ajouter une étiquette à la page ASP.NET, en définissant sa ID propriété ExceptionDetails sur et en supprimant sa Text propriété. Pour attirer l’attention de l’utilisateur sur ce message, définissez sa CssClass propriété sur Warning, qui est une classe CSS que nous avons ajoutée au Styles.css fichier dans le tutoriel précédent. Rappelez-vous que cette classe CSS entraîne l’affichage du texte de l’étiquette dans une police rouge, italique, gras et très grande.

Ajouter un contrôle Web d’étiquette à la page

Figure 7 : Ajouter un contrôle Web d’étiquette à la page (cliquer pour afficher l’image en taille réelle)

Étant donné que nous voulons que ce contrôle Label Web ne soit visible qu’immédiatement après qu’une exception s’est produite, définissez sa Visible propriété sur false dans le Page_Load gestionnaire d’événements :

Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load
    ExceptionDetails.Visible = False
End Sub

Avec ce code, sur la première page visite et les publications suivantes, la propriété du ExceptionDetails contrôle est Visible définie falsesur . Face à une exception de niveau DAL ou BLL, que nous pouvons détecter dans le gestionnaire d’événements gridViewRowUpdated, nous allons définir la propriété du Visible contrôle sur ExceptionDetails true. Étant donné que les gestionnaires d’événements de contrôle Web se produisent après le Page_Load gestionnaire d’événements dans le cycle de vie de la page, l’étiquette s’affiche. Toutefois, lors de la publication suivante, le Page_Load gestionnaire d’événements rétablit la Visible propriété à , la masquant à falsenouveau de l’affichage.

Notes

Nous pourrions également supprimer la nécessité de définir la propriété du Visible contrôle dans en Page_Load affectant sa Visible propriété false dans la syntaxe déclarative et en désactivant son état d’affichage (affectant à sa EnableViewState propriété la valeur false).ExceptionDetails Nous utiliserons cette autre approche dans un prochain tutoriel.

Une fois le contrôle Label ajouté, l’étape suivante consiste à créer le gestionnaire d’événements pour l’événement GridView RowUpdated . Sélectionnez gridView dans le Designer, accédez à la Fenêtre Propriétés, puis cliquez sur l’icône éclair, répertoriant les événements de GridView. Il doit déjà y avoir une entrée pour l’événement RowUpdating GridView, car nous avons créé un gestionnaire d’événements pour cet événement plus haut dans ce tutoriel. Créez également un gestionnaire d’événements pour l’événement RowUpdated .

Créer un gestionnaire d’événements pour l’événement RowUpdated de GridView

Figure 8 : Créer un gestionnaire d’événements pour l’événement GridView RowUpdated

Notes

Vous pouvez également créer le gestionnaire d’événements via les listes déroulantes en haut du fichier de classe code-behind. Sélectionnez GridView dans la liste déroulante à gauche et l’événement RowUpdated à droite.

La création de ce gestionnaire d’événements ajoute le code suivant à la classe code-behind de la page ASP.NET :

Protected Sub GridView1_RowUpdated(ByVal sender As Object, _
    ByVal e As System.Web.UI.WebControls.GridViewUpdatedEventArgs) _
    Handles GridView1.RowUpdated
End Sub

Le deuxième paramètre d’entrée de ce gestionnaire d’événements est un objet de type GridViewUpdatedEventArgs, qui a trois propriétés intéressantes pour la gestion des exceptions :

  • Exception référence à l’exception levée ; si aucune exception n’a été levée, cette propriété aura la valeur null
  • ExceptionHandled valeur booléenne qui indique si l’exception a été gérée ou non dans le RowUpdated gestionnaire d’événements ; si false (la valeur par défaut), l’exception est levée de nouveau, percolant jusqu’au ASP.NET runtime
  • KeepInEditMode si la true ligne GridView modifiée reste en mode édition ; si false (par défaut), la ligne GridView revient à son mode lecture seule

Notre code doit donc case activée pour voir si Exception n’est pas null, ce qui signifie qu’une exception a été levée lors de l’exécution de l’opération. Si c’est le cas, nous voulons :

  • Afficher un message convivial dans l’étiquette ExceptionDetails
  • Indiquer que l’exception a été gérée
  • Conserver la ligne GridView en mode édition

Le code suivant permet d’atteindre ces objectifs :

Protected Sub GridView1_RowUpdated(ByVal sender As Object, _
    ByVal e As System.Web.UI.WebControls.GridViewUpdatedEventArgs) _
    Handles GridView1.RowUpdated
    If e.Exception IsNot Nothing Then
        ExceptionDetails.Visible = True
        ExceptionDetails.Text = "There was a problem updating the product. "
        If e.Exception.InnerException IsNot Nothing Then
            Dim inner As Exception = e.Exception.InnerException
            If TypeOf inner Is System.Data.Common.DbException Then
                ExceptionDetails.Text &= _
                "Our database is currently experiencing problems." & _
                "Please try again later."
            ElseIf TypeOf inner _
             Is System.Data.NoNullAllowedException Then
                ExceptionDetails.Text += _
                    "There are one or more required fields that are missing."
            ElseIf TypeOf inner Is ArgumentException Then
                Dim paramName As String = CType(inner, ArgumentException).ParamName
                ExceptionDetails.Text &= _
                    String.Concat("The ", paramName, " value is illegal.")
            ElseIf TypeOf inner Is ApplicationException Then
                ExceptionDetails.Text += inner.Message
            End If
        End If
        e.ExceptionHandled = True
        e.KeepInEditMode = True
    End If
End Sub

Ce gestionnaire d’événements commence par vérifier si e.Exception est null. Si ce n’est pas le cas, la ExceptionDetails propriété label Visible a la true valeur et sa Text propriété a la valeur « Il y a eu un problème de mise à jour du produit ». Les détails de l’exception réelle levée résident dans la propriété de l’objet e.ExceptionInnerException . Cette exception interne est examinée et, si elle est d’un type particulier, un message supplémentaire utile est ajouté à la ExceptionDetails propriété du Text Label. Enfin, les ExceptionHandled propriétés et KeepInEditMode sont toutes deux définies sur true.

La figure 9 montre une capture d’écran de cette page lorsque vous omettez le nom du produit ; La figure 10 montre les résultats lors de l’entrée d’une valeur non valide UnitPrice (-50).

Le champ boundField ProductName doit contenir une valeur

Figure 9 : Le ProductName champ BoundField doit contenir une valeur (cliquer pour afficher l’image de taille réelle)

Les valeurs UnitPrice négatives ne sont pas autorisées

Figure 10 : Les valeurs négatives UnitPrice ne sont pas autorisées (cliquer pour afficher l’image en taille réelle)

En définissant la e.ExceptionHandled propriété sur true, le RowUpdated gestionnaire d’événements a indiqué qu’il a géré l’exception. Par conséquent, l’exception ne se propage pas jusqu’au ASP.NET runtime.

Notes

Les figures 9 et 10 montrent un moyen gracieux de gérer les exceptions levées en raison d’une entrée utilisateur non valide. Dans l’idéal, toutefois, cette entrée non valide n’atteindra jamais la couche logique métier en premier lieu, car la page ASP.NET doit s’assurer que les entrées de l’utilisateur sont valides avant d’appeler la méthode de UpdateProduct la ProductsBLL classe. Dans notre prochain tutoriel, nous allons voir comment ajouter des contrôles de validation aux interfaces de modification et d’insertion pour vérifier que les données envoyées à la couche logique métier sont conformes aux règles métier. Les contrôles de validation empêchent non seulement l’appel de la UpdateProduct méthode jusqu’à ce que les données fournies par l’utilisateur soient valides, mais ils fournissent également une expérience utilisateur plus informative pour identifier les problèmes de saisie des données.

Étape 3 : Gestion correcte des exceptions BLL-Level

Lors de l’insertion, de la mise à jour ou de la suppression de données, la couche d’accès aux données peut lever une exception en cas d’erreur liée aux données. La base de données peut être hors connexion, une colonne de table de base de données requise n’a peut-être pas eu une valeur spécifiée ou une contrainte au niveau de la table peut avoir été violée. En plus des exceptions strictement liées aux données, la couche logique métier peut utiliser des exceptions pour indiquer quand des règles métier ont été violées. Dans le didacticiel Création d’une couche logique métier, par exemple, nous avons ajouté une règle métier case activée à la surcharge d’origineUpdateProduct. Plus précisément, si l’utilisateur marque un produit comme étant abandonné, nous avons exigé que le produit ne soit pas le seul fourni par son fournisseur. Si cette condition a été violée, un ApplicationException a été levée.

Pour la UpdateProduct surcharge créée dans ce tutoriel, nous allons ajouter une règle métier qui empêche le UnitPrice champ d’être défini sur une nouvelle valeur qui est plus de deux fois la valeur d’origine UnitPrice . Pour ce faire, ajustez la UpdateProduct surcharge afin qu’elle effectue cette case activée et lève un ApplicationException si la règle est violée. La méthode mise à jour est la suivante :

<System.ComponentModel.DataObjectMethodAttribute _
    (System.ComponentModel.DataObjectMethodType.Update, True)> _
    Public Function UpdateProduct(ByVal productName As String, _
    ByVal unitPrice As Nullable(Of Decimal), ByVal unitsInStock As Nullable(Of Short), _
    ByVal productID As Integer) As Boolean
    Dim products As Northwind.ProductsDataTable = Adapter.GetProductByProductID(productID)
    If products.Count = 0 Then
        Return False
    End If
    Dim product As Northwind.ProductsRow = products(0)
    If unitPrice.HasValue AndAlso Not product.IsUnitPriceNull() Then
        If unitPrice > product.UnitPrice * 2 Then
            Throw New ApplicationException( _
                "When updating a product price," & _
                " the new price cannot exceed twice the original price.")
        End If
    End If
    product.ProductName = productName
    If Not unitPrice.HasValue Then
        product.SetUnitPriceNull()
    Else
        product.UnitPrice = unitPrice.Value
    End If
    If Not unitsInStock.HasValue Then
        product.SetUnitsInStockNull()
    Else
        product.UnitsInStock = unitsInStock.Value
    End If
    Dim rowsAffected As Integer = Adapter.Update(product)
    Return rowsAffected = 1
End Function

Avec cette modification, toute mise à jour de prix supérieure à deux fois le prix existant entraîne la levée d’un ApplicationException . Tout comme l’exception levée à partir du DAL, cette BLL levée ApplicationException peut être détectée et gérée dans le gestionnaire d’événements gridView RowUpdated . En fait, le RowUpdated code du gestionnaire d’événements, tel qu’il est écrit, détecte correctement cette exception et affiche la valeur de la ApplicationExceptionpropriété .Message La figure 11 montre une capture d’écran lorsqu’un utilisateur tente de mettre à jour le prix de Chai à 50,00 $, soit plus du double de son prix actuel de 19,95 $.

Les règles commerciales interdisent les augmentations de prix qui dépassent le double du prix d’un produit

Figure 11 : Les règles d’entreprise interdisent les augmentations de prix qui dépassent le double du prix d’un produit (cliquez pour afficher l’image en taille réelle)

Notes

Dans l’idéal UpdateProduct , nos règles de logique métier seraient refactorisée en dehors des surcharges de méthode et dans une méthode commune. Il s’agit d’un exercice pour le lecteur.

Résumé

Lors des opérations d’insertion, de mise à jour et de suppression, le contrôle Web de données et ObjectDataSource ont impliqué le déclenchement d’événements de pré et de post-niveau qui réservent l’opération réelle. Comme nous l’avons vu dans ce tutoriel et dans le précédent, lors de l’utilisation d’un GridView modifiable, l’événement GridView se RowUpdating déclenche, suivi de l’événement Updating ObjectDataSource, auquel cas la commande de mise à jour est effectuée sur l’objet sous-jacent de ObjectDataSource. Une fois l’opération terminée, l’événement ObjectDataSource se Updated déclenche, suivi de l’événement GridView RowUpdated .

Nous pouvons créer des gestionnaires d’événements pour les événements de pré-niveau afin de personnaliser les paramètres d’entrée ou pour les événements post-niveau afin d’inspecter et de répondre aux résultats de l’opération. Les gestionnaires d’événements post-niveau sont le plus couramment utilisés pour détecter si une exception s’est produite pendant l’opération. En cas d’exception, ces gestionnaires d’événements post-niveau peuvent éventuellement gérer l’exception seuls. Dans ce tutoriel, nous avons vu comment gérer une telle exception en affichant un message d’erreur convivial.

Dans le tutoriel suivant, nous allons voir comment réduire la probabilité d’exceptions résultant de problèmes de mise en forme des données (par exemple, la saisie d’un élément négatif UnitPrice). Plus précisément, nous allons voir comment ajouter des contrôles de validation aux interfaces de modification et d’insertion.

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.

Un merci spécial à

Cette série de tutoriels a été examinée par de nombreux réviseurs utiles. La réviseure principale de ce tutoriel était Liz Shulok. Vous souhaitez consulter mes prochains articles MSDN ? Si c’est le cas, déposez-moi une ligne à mitchell@4GuysFromRolla.com.