Gestion des exceptions de niveau BLL et DAL dans une page ASP.NET (C#)
par Scott Mitchell
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 :
- 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.
- Appelez la méthode.
- 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.
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 ProductName
champs , QuantityPerUnit
, UnitPrice
et 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 false
et sa ApplyFormatInEditMode
sur true
, comme illustré dans la figure 2.
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
.
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.
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 ProductName
champs , UnitPrice
et UnitsInStock
peuvent être modifiés.UnitsInStock
UnitPrice
QuantityPerUnit
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
.
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.
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
.
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 valeurnull
ExceptionHandled
valeur booléenne qui indique si l’exception a été gérée ou non dans leRowUpdated
gestionnaire d’événements ; sifalse
(valeur par défaut), l’exception est levée de nouveau, percolant jusqu’au ASP.NET runtimeKeepInEditMode
si latrue
ligne GridView modifiée reste en mode édition ; sifalse
(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.Exception
InnerException
. 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).
Figure 9 : Le ProductName
champ BoundField doit contenir une valeur (cliquer pour afficher l’image de taille réelle)
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 ApplicationException
proprié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 $.
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.