Notes
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
par Scott Mitchell
Dans ce tutoriel, nous allons voir comment afficher un message d’erreur convivial et informatif si une exception se produit lors d’une opération d’insertion, de mise à jour ou de suppression d’un contrôle web de données ASP.NET.
Présentation
L’utilisation de données à partir 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 logique métier à appeler 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 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 des données, cela peut inclure l’exécution d’une action basée sur une valeur de retour ou la gestion normale de toute exception qui s’est produite à l’étape 2.
Comme nous l’avons vu dans le didacticiel précédent, les contrôles ObjectDataSource et web de données 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 UpdateParameters
champ à sa collection ObjectDataSource ; son RowUpdated
événement est déclenché une fois l’opération terminée.
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 attirer l’attention sur les événements qui se déclenchent une fois l’opération terminée. Avec ces gestionnaires d’événements de post-niveau, nous pouvons, entre autres, déterminer si une exception s’est produite pendant l’opération et la gérer correctement, affichant un message d’erreur convivial et informatif à l’écran plutôt que de passer par défaut à la page d’exception ASP.NET standard.
Pour illustrer l’utilisation de ces événements de 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 message court au-dessus de GridView expliquant qu’un problème s’est produit. Commençons !
Étape 1 : Création d’un GridView modifiable des produits
Dans le tutoriel précédent, nous avons créé un GridView modifiable avec seulement deux champs et ProductName
UnitPrice
. Cela nécessite la création d'une surcharge supplémentaire pour la méthode de la classe ProductsBLL
, une qui accepte seulement trois paramètres d'entrée (le nom du produit, le prix unitaire et l'identifiant) au lieu d'un paramètre pour chaque champ du 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 autorise uniquement le nom, le prix unitaire et les unités en stock à modifier.
Pour prendre en charge ce scénario, nous aurons 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;
}
Avec 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 Concepteur. Liez le GridView à un nouvel ObjectDataSource, en mappant la méthode Select()
à la méthode ProductsBLL
de la classe GetProducts()
et la méthode Update()
à la surcharge UpdateProduct
qui vient d'être créée.
Figure 1 : Utiliser la surcharge de méthode UpdateProduct
qui accepte quatre paramètres d’entrée (cliquez pour afficher l’image de taille complète)
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 attribue à la propriété OldValuesParameterFormatString
la valeur original_{0}
, ce qui entraînera une exception, car les classes BLL ne s'attendent pas à recevoir un paramètre d’entrée nommé original_productID
. 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, réduisez le GridView pour inclure uniquement les ProductName
champs, QuantityPerUnit
champs, UnitPrice
champs, et UnitsInStock
BoundFields. 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 UnitPrice
BoundField en tant que devise, à la 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é du BoundField en DataFormatString
à {0:c}
, sa propriété HtmlEncode
à false
, et sa propriété ApplyFormatInEditMode
à true
, comme illustré dans la Figure 2.
Figure 2 : Configurer UnitPrice
BoundField pour qu’il s’affiche sous forme de devise (cliquez pour afficher l’image de taille complète)
La mise en forme de UnitPrice
en devise dans l'interface d'édition nécessite la création d'un gestionnaire d'événements pour l'événement RowUpdating
qui convertit la chaîne formatée en devise en une valeur decimal
. Rappelez-vous que le RowUpdating
gestionnaire d’événements du dernier didacticiel a également vérifié afin de s'assurer 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
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
.
Figure 3 : Créez le BoundField Read-Only (QuantityPerUnit
)
Enfin, cochez la case Activer la modification depuis la balise intelligente de GridView. Une fois ces étapes effectuées, le Designer de la ErrorHandling.aspx
page devrait ressembler à la Figure 4.
Figure 4 : Supprimer toutes les limites requises et cochez la case Activer la modification (cliquez pour afficher l’image de taille complète)
À ce stade, nous avons une liste des champs ProductName
, QuantityPerUnit
, UnitPrice
et UnitsInStock
de tous les produits ; toutefois, seuls les champs ProductName
, UnitPrice
et UnitsInStock
peuvent être modifiés.
Figure 5 : Les utilisateurs peuvent désormais modifier facilement les noms, les prix et les unités des produits dans les champs Stock (cliquez pour afficher l’image de taille complète)
Étape 2 : Gestion normale des exceptions DAL-Level
Bien que notre GridView modifiable fonctionne merveilleusement lorsque les utilisateurs entrent des valeurs légales pour le nom, le prix et les unités du produit modifiés en stock, l’entrée de valeurs illégales entraîne une exception. Par exemple, omettre la valeur ProductName
entraîne la levée d’une NoNullAllowedException parce que la propriété ProductName
dans la classe ProductsRow
a sa propriété AllowDBNull
définie à false
; si la base de données est en panne, une SqlException
sera levée par le TableAdapter lorsqu'on tente de se connecter à la base de données. Sans effectuer d’action, ces exceptions s’affichent 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 localhost
ou non, 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. 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 localhost
.
Figure 6 : L’omission du nom du produit affiche les détails de l’exception (cliquez pour afficher l’image de taille complète)
Bien que ces détails d’exception soient utiles lors du test d’une application, la présentation d’un utilisateur final avec un tel écran face à une exception est inférieure à l’idéal. Un utilisateur final ne sait probablement pas ce qu’est NoNullAllowedException
ou pourquoi il a été provoqué. Une meilleure approche consiste à présenter à l’utilisateur un message plus convivial expliquant qu’il y avait 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 niveau post dans ObjectDataSource et le contrôle Web de données fournissent un moyen de la détecter et d’annuler l’exception avant qu'elle ne remonte jusqu'à l'exécution ASP.NET. 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, le cas échéant, affiche les détails de l’exception dans un contrôle Web Label.
Commencez par ajouter une étiquette à la page ASP.NET, en définissant sa ID
propriété ExceptionDetails
et en supprimant sa Text
propriété. Pour attirer l'attention de l'utilisateur sur ce message, définissez sa propriété CssClass
sur Warning
, qui est une classe CSS que nous avons ajoutée dans le fichier Styles.css
lors du didacticiel précédent. Rappelez-vous que cette classe CSS entraîne l'affichage du texte de l'étiquette en rouge, italique, gras et très grand.
Figure 7 : Ajouter un contrôle Web d’étiquette à la page (cliquez pour afficher l’image de taille complète)
Étant donné que nous voulons que ce contrôle Label Web soit visible uniquement 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 void Page_Load(object sender, EventArgs e)
{
ExceptionDetails.Visible = false;
}
Avec ce code, lors de la première visite de la page et des postbacks ultérieurs, le contrôle ExceptionDetails
aura sa propriété Visible
définie sur false
. Face à une exception de niveau DAL ou BLL, que nous pouvons détecter dans le gestionnaire d’événements RowUpdated
de GridView, nous allons définir la propriété ExceptionDetails
du contrôle Visible
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, le Label s’affiche. Toutefois, lors de la prochaine publication, le gestionnaire d'événements Page_Load
rétablira la propriété Visible
à false
, la masquant à nouveau de l'affichage.
Remarque
Nous pourrions également supprimer la nécessité de définir la propriété ExceptionDetails
du contrôle Visible
en Page_Load
en affectant sa propriété Visible
à false
dans la syntaxe déclarative et en désactivant son état d’affichage (en définissant sa propriété EnableViewState
sur false
). Nous allons utiliser cette approche alternative dans un prochain tutoriel.
Avec le contrôle Label ajouté, notre étape suivante consiste à créer le gestionnaire d’événements pour l’événement RowUpdated
gridView. Sélectionnez GridView dans le Concepteur, 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 GridView, car nous avons créé un gestionnaire d’événements RowUpdating
pour cet événement précédemment 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 RowUpdated
GridView
Remarque
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 la liste 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 une valeur denull
-
ExceptionHandled
valeur booléenne qui indique si l’exception a été gérée ou non dans leRowUpdated
gestionnaire d’événements ; sifalse
(la valeur par défaut), l’exception est levée à nouveau, encolant jusqu’au runtime ASP.NET -
KeepInEditMode
si elle est définie surtrue
la ligne GridView modifiée reste en mode édition ; sifalse
(valeur par défaut), la ligne GridView revient à son mode lecture seule
Notre code doit alors vérifier 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
Ce code suivant accomplit 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;
}
}
Le gestionnaire d’événements commence par vérifier si e.Exception
est null
. Si ce n’est pas le cas, la propriété ExceptionDetails
de l'étiquette Visible
est définie sur true
, et sa propriété Text
sur « 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é e.Exception
de l’objet InnerException
. Cette exception interne est examinée et, s’il s’agit d’un type particulier, un message supplémentaire et utile est ajouté à la propriété ExceptionDetails
de l’étiquette Text
. Enfin, les propriétés ExceptionHandled
et KeepInEditMode
sont toutes deux définies sur true
.
La figure 9 montre une capture d’écran de cette page lors de l’omission du nom du produit ; La figure 10 montre les résultats lors de l’entrée d’une valeur illégale UnitPrice
(-50).
Figure 9 : BoundField ProductName
doit contenir une valeur (cliquez pour afficher l’image de taille complète)
Figure 10 : Les valeurs négatives UnitPrice
ne sont pas autorisées (cliquez pour afficher l’image de taille complète)
En définissant la propriété e.ExceptionHandled
à true
, le gestionnaire d’événements RowUpdated
a indiqué qu’il a géré l’exception. Par conséquent, l’exception ne se propage pas au runtime ASP.NET.
Remarque
Les figures 9 et 10 montrent un moyen approprié de gérer les exceptions levées en raison d’une entrée utilisateur non valide. Dans l’idéal, toutefois, une telle entrée non valide n’atteint 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 ProductsBLL
la UpdateProduct
classe. Dans notre prochain tutoriel, nous allons voir comment ajouter des contrôles de validation aux interfaces d’édition et d’insertion pour vous assurer que les données soumises à 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 fournissent également une expérience utilisateur plus informative pour identifier les problèmes d’entrée de données.
Étape 3 : Gestion normale 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 face à une 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 d’entreprise ont été violées. Dans le didacticiel Création d’une couche logique métier , par exemple, nous avons ajouté une vérification de règle métier à la surcharge d’origine UpdateProduct
. Plus précisément, si l’utilisateur marque un produit comme supprimé, nous avons exigé que le produit ne soit pas le seul fourni par son fournisseur. Si cette condition a été violée, une ApplicationException
exception a été levée.
Pour la UpdateProduct
surcharge créée dans ce tutoriel, nous allons ajouter une règle d’entreprise qui empêche le UnitPrice
champ d’être défini sur une nouvelle valeur qui est supérieure à deux fois la valeur d’origine UnitPrice
. Pour ce faire, ajustez la UpdateProduct
surcharge afin qu’elle effectue cette vérification et lève une ApplicationException
si la règle est violée. La méthode mise à jour suit :
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 qui est supérieure à deux fois le prix existant provoquera le déclenchement d'une ApplicationException
. Tout comme l’exception levée à partir de la DAL, cette exception levée par le BLL ApplicationException
peut être détectée et gérée dans le gestionnaire d’événements RowUpdated
de GridView. En fait, le code du gestionnaire d’événements RowUpdated
, tel qu'il est écrit, détecte correctement cette exception et affiche la valeur de la propriété ApplicationException
Message
. La figure 11 montre une capture d’écran lorsqu’un utilisateur tente de mettre à jour le prix de Chai à 50,00 $, soit plus que le 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 pleine taille)
Remarque
Dans l’idéal, nos règles de logique métier seraient refactorisées en dehors des surcharges de méthode UpdateProduct
et intégrées dans une méthode commune. Ceci est laissé en tant qu’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 l'ObjectDataSource déclenchent des événements préalables et postérieurs qui encadrent l'opération réelle. Comme nous l’avons vu dans ce didacticiel et le précédent, lorsque nous travaillons avec un GridView modifiable, l’événement RowUpdating
GridView se déclenche, suivi de Updating
l’événement ObjectDataSource, auquel point la commande de mise à jour est effectuée sur l’objet sous-jacent d’ObjectDataSource. Une fois l’opération terminée, l’événement Updated
ObjectDataSource se déclenche, suivi de l’événement RowUpdated
gridView.
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 les événements de post-niveau afin d’inspecter et de répondre aux résultats de l’opération. Les gestionnaires d’événements de post-niveau sont les plus couramment utilisés pour détecter si une exception s’est produite pendant l’opération. Face à une exception, ces gestionnaires d’événements de post-niveau peuvent éventuellement gérer l’exception par eux-mêmes. 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, entrer un négatif UnitPrice
). Plus précisément, nous allons examiner comment ajouter des contrôles de validation aux interfaces d’édition 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 en tant que consultant indépendant, formateur et écrivain. Son dernier livre est Sams Teach Yourself ASP.NET 2.0 en 24 heures. On peut le joindre à mitchell@4GuysFromRolla.com.
Merci spécial à
Cette série de tutoriels a été examinée par de nombreux réviseurs utiles. Le réviseur principal 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.