Partager via


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

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 la méthode de la couche de logique métier qui doit être appelée et les valeurs de paramètre à passer. Les valeurs des paramètres peuvent être codées en dur, affectées par programmation ou 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 les données, cela peut inclure l’exécution d’une action basée sur une valeur de retour ou la gestion appropriée 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 à sa collection ObjectDataSource UpdateParameters ; son RowUpdated événement est déclenché une fois que l’ObjetDataSource a terminé l’opération.

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 nous intéresser aux é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, nous allons créer 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 bref message au-dessus de 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 refaire 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 avons besoin d’une autre surcharge de la UpdateProduct méthode, une 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, false)]
public bool UpdateProduct(string productName, decimal? unitPrice, short? unitsInStock,
    int productID)
{
    Northwind.ProductsDataTable products = Adapter.GetProductByProductID(productID);
    if (products.Count == 0)
        // no matching record found, return false
        return false;
    Northwind.ProductsRow product = products[0];
    product.ProductName = productName;
    if (unitPrice == null) product.SetUnitPriceNull();
      else product.UnitPrice = unitPrice.Value;
    if (unitsInStock == null) product.SetUnitsInStockNull();
      else product.UnitsInStock = unitsInStock.Value;
    // Update the product record
    int rowsAffected = Adapter.Update(product);
    // Return true if precisely one row was updated, otherwise false
    return rowsAffected == 1;
}

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 que vous venez de créer.

Utiliser la surcharge de méthode UpdateProduct 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 de l’ObjectDataSource affecte à la OldValuesParameterFormatString propriété la valeur original_{0}, ce qui provoquera une exception, car nos classes BLL ne s’attendent pas à ce qu’un paramètre d’entrée nommé original_productID soit passé. N’oubliez pas de supprimer complètement ce paramètre de la syntaxe déclarative (ou définissez-le sur la valeur par défaut, {0}).

Ensuite, analysez 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 champ BoundField en tant que devise à la UnitPrice fois en mode lecture seule et en mode édition. Faisons la même chose ici. Rappelez-vous que cela nécessitait de définir la propriété de DataFormatString BoundField sur {0:c}, sa HtmlEncode propriété sur falseet sa ApplyFormatInEditMode sur true, comme illustré dans la figure 2.

Configurer l’objet BoundField UnitPrice pour qu’il s’affiche en tant que devise

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

La mise en forme de en UnitPrice tant que devise dans l’interface d’édition 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 dans une decimal valeur. Rappelez-vous que le RowUpdating gestionnaire d’événements du dernier didacticiel 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 void GridView1_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
    if (e.NewValues["UnitPrice"] != null)
        e.NewValues["UnitPrice"] =decimal.Parse(e.NewValues["UnitPrice"].ToString(),
            System.Globalization.NumberStyles.Currency);
}

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

Rendre l’objet BoundField QuantityPerUnit en lecture seule

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

Enfin, case activée la case Activer la modification de la balise active de GridView. Après avoir effectué ces étapes, le ErrorHandling.aspx Designer de la page doit ressembler à la figure 4.

Supprimez tout sauf les champs limités nécessaires et cochez la case Activer l’édition

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

À ce stade, nous avons une liste des champs , , et de tous les produits ProductName. Toutefois, seuls les ProductNamechamps , UnitPriceet UnitsInStock peuvent être modifiés.UnitsInStockUnitPriceQuantityPerUnit

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

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

Étape 2 : Gestion appropriée 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 valeur false; si la base de données est en panne, un SqlException est levée par l’objet TableAdapter lors de la tentative de connexion à la base de données. Sans effectuer aucune action, ces exceptions s’affichent de la couche d’accès aux données à la couche de logique métier, puis à la page ASP.NET et enfin au 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 gérée peut entraîner une page d’erreur de serveur générique, un rapport d’erreurs détaillé ou une page web conviviale. Pour plus d’informations sur la façon dont le runtime ASP.NET répond à une exception non interceptée, consultez Gestion des erreurs d’application web dans ASP.NET et l’élément customErrors .

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.

L’omission du 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 de post-niveau dans le contrôle ObjectDataSource et le contrôle Web de données fournissent un moyen de la détecter et d’annuler la propagation de l’exception au ASP.NET runtime. Pour notre exemple, nous allons créer un gestionnaire d’événements pour l’événement RowUpdated GridView qui détermine si une exception a été déclenchée et, si c’est 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 affectant à ExceptionDetails sa ID propriété la valeur 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 volumineuse.

Ajouter un contrôle Web Label à la page

Figure 7 : Ajouter un contrôle Web Label à 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, affectez à sa Visible propriété la valeur false dans le Page_Load gestionnaire d’événements :

protected void Page_Load(object sender, EventArgs e)
{
    ExceptionDetails.Visible = false;
}

Avec ce code, sur la première visite de page et les publications ultérieures, la propriété du ExceptionDetails contrôle est Visible définie sur false. En cas d’exception de niveau DAL ou BLL, que nous pouvons détecter dans le gestionnaire d’événements de RowUpdated GridView, nous allons définir la ExceptionDetails propriété du Visible contrôle sur 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é en false, la masquant à nouveau 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 (en définissant sa EnableViewState propriété sur 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 à partir de celle de droite.

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

protected void GridView1_RowUpdated(object sender, GridViewUpdatedEventArgs e)
{
}

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 (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 (valeur 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 void GridView1_RowUpdated(object sender, GridViewUpdatedEventArgs e)
{
    if (e.Exception != null)
    {
        // Display a user-friendly message
        ExceptionDetails.Visible = true;
        ExceptionDetails.Text = "There was a problem updating the product. ";
        if (e.Exception.InnerException != null)
        {
            Exception inner = e.Exception.InnerException;
            if (inner is System.Data.Common.DbException)
                ExceptionDetails.Text +=
                    "Our database is currently experiencing problems." +
                    "Please try again later.";
            else if (inner is NoNullAllowedException)
                ExceptionDetails.Text +=
                    "There are one or more required fields that are missing.";
            else if (inner is ArgumentException)
            {
                string paramName = ((ArgumentException)inner).ParamName;
                ExceptionDetails.Text +=
                    string.Concat("The ", paramName, " value is illegal.");
            }
            else if (inner is ApplicationException)
                ExceptionDetails.Text += inner.Message;
        }
        // Indicate that the exception has been handled
        e.ExceptionHandled = true;
        // Keep the row in edit mode
        e.KeepInEditMode = true;
    }
}

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 :

public bool UpdateProduct(string productName, decimal? unitPrice, short? unitsInStock,
    int productID)
{
    Northwind.ProductsDataTable products = Adapter.GetProductByProductID(productID);
    if (products.Count == 0)
        // no matching record found, return false
        return false;
    Northwind.ProductsRow product = products[0];
    // Make sure the price has not more than doubled
    if (unitPrice != null && !product.IsUnitPriceNull())
        if (unitPrice > product.UnitPrice * 2)
          throw new ApplicationException(
            "When updating a product price," +
            " the new price cannot exceed twice the original price.");
    product.ProductName = productName;
    if (unitPrice == null) product.SetUnitPriceNull();
      else product.UnitPrice = unitPrice.Value;
    if (unitsInStock == null) product.SetUnitsInStockNull();
      else product.UnitsInStock = unitsInStock.Value;
    // Update the product record
    int rowsAffected = Adapter.Update(product);
    // Return true if precisely one row was updated, otherwise false
    return rowsAffected == 1;
}

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.