Gestione delle eccezioni a livello BLL e DAL in una pagina ASP.NET (C#)

di Scott Mitchell

Scarica il PDF

In questa esercitazione verrà illustrato come visualizzare un messaggio di errore descrittivo e informativo deve verificarsi un'eccezione durante un'operazione di inserimento, aggiornamento o eliminazione di un controllo Web di ASP.NET dati.

Introduzione

L'uso dei dati da un'applicazione Web ASP.NET tramite un'architettura di applicazioni a livelli prevede i tre passaggi generali seguenti:

  1. Determinare quale metodo del livello di logica di business deve essere richiamato e quali valori di parametro passare. I valori dei parametri possono essere codificati, assegnati a livello di codice a livello di codice o input immessi dall'utente.
  2. Richiamare il metodo.
  3. Elaborare i risultati. Quando si chiama un metodo BLL che restituisce i dati, ciò può comportare l'associazione dei dati a un controllo Web dati. Per i metodi BLL che modificano i dati, questa operazione può includere l'esecuzione di alcune azioni in base a un valore restituito o alla gestione di qualsiasi eccezione generata nel passaggio 2.

Come illustrato nell'esercitazione precedente, sia i controlli ObjectDataSource che i controlli Web dati forniscono punti di estendibilità per i passaggi 1 e 3. GridView, ad esempio, genera l'evento RowUpdating prima di assegnare i relativi valori di campo all'insieme ObjectDataSource. RowUpdated L'evento viene generato dopo che ObjectDataSource UpdateParameters ha completato l'operazione.

Sono già stati esaminati gli eventi che vengono attivati durante il passaggio 1 e hanno visto come possono essere usati per personalizzare i parametri di input o annullare l'operazione. In questa esercitazione si esaminerà l'attenzione sugli eventi che vengono attivati dopo il completamento dell'operazione. Con questi gestori eventi a livello post-livello è possibile, tra le altre cose, determinare se si è verificata un'eccezione durante l'operazione e gestirla in modo normale, visualizzando un messaggio di errore descrittivo e informativo sullo schermo anziché per impostazione predefinita alla pagina di eccezione ASP.NET standard.

Per illustrare l'uso di questi eventi di post-livello, verrà creata una pagina che elenca i prodotti in una griglia modificabile. Quando si aggiorna un prodotto, se viene generata un'eccezione, la pagina ASP.NET visualizzerà un breve messaggio sopra GridView che spiega che si è verificato un problema. È possibile iniziare subito.

Passaggio 1: Creazione di una griglia modificabile di prodotti

Nell'esercitazione precedente è stato creato un controllo GridView modificabile con solo due campi ProductName e UnitPrice. Ciò richiedeva la creazione di un overload aggiuntivo per il ProductsBLL metodo della UpdateProduct classe, uno che accettava solo tre parametri di input (il nome del prodotto, il prezzo unitario e l'ID) anziché un parametro per ogni campo del prodotto. Per questa esercitazione, si pratica di nuovo questa tecnica, creando un gridView modificabile che visualizza il nome del prodotto, la quantità per unità, il prezzo unitario e le unità in magazzino, ma consente di modificare solo il nome, il prezzo unitario e le unità in magazzino.

Per supportare questo scenario, sarà necessario un altro overload del UpdateProduct metodo, uno che accetta quattro parametri: il nome del prodotto, il prezzo unitario, le unità in magazzino e l'ID. Aggiungere il metodo seguente alla 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;
}

Con questo metodo è possibile creare la pagina ASP.NET che consente di modificare questi quattro campi di prodotto specifici. Aprire la ErrorHandling.aspx pagina nella EditInsertDelete cartella e aggiungere GridView alla pagina tramite il Designer. Associare GridView a un nuovo OggettoDataSource, mapping Select() del metodo al ProductsBLL metodo della GetProducts() classe e al Update() metodo all'overload UpdateProduct appena creato.

Usare l'overload del metodo UpdateProduct che accetta quattro parametri di input

Figura 1: Usare l'overload del metodo che accetta quattro parametri di input (fare clic per visualizzare l'immagineUpdateProduct full-size)

Verrà creato un oggetto ObjectDataSource con una UpdateParameters raccolta con quattro parametri e un oggetto GridView con un campo per ognuno dei campi del prodotto. Il markup dichiarativo di ObjectDataSource assegna alla proprietà il valore original_{0}, che causerà un'eccezione perché la OldValuesParameterFormatString classe BLL non prevede che venga passato un parametro di input denominatooriginal_productID. Non dimenticare di rimuovere completamente questa impostazione dalla sintassi dichiarativa (o impostarla sul valore predefinito, {0}).

Successivamente, pare giù GridView per includere solo , ProductName, QuantityPerUnitUnitPricee UnitsInStock BoundFields. Inoltre, è possibile applicare qualsiasi formattazione a livello di campo che si ritiene necessaria (ad esempio la modifica delle HeaderText proprietà).

Nell'esercitazione UnitPrice precedente è stato illustrato come formattare BoundField come valuta sia in modalità di sola lettura che in modalità di modifica. Facciamo la stessa cosa qui. Tenere presente che questa impostazione obbligatoria della DataFormatString proprietà BoundField su , la relativa HtmlEncode proprietà {0:c}su falsee il relativo ApplyFormatInEditMode su true, come illustrato nella figura 2.

Configurare UnitPrice BoundField per visualizzare come valuta

Figura 2: Configurare BoundField per visualizzare come valuta (fare clic per visualizzare l'immagineUnitPrice a dimensioni complete)

La formattazione UnitPrice di come valuta nell'interfaccia di modifica richiede la creazione di un gestore eventi per l'evento RowUpdating gridView che analizza la stringa formattata in valuta in un decimal valore. Tenere presente che il RowUpdating gestore eventi dell'ultima esercitazione ha controllato anche per assicurarsi che l'utente ha fornito un UnitPrice valore. Tuttavia, per questa esercitazione è possibile consentire all'utente di omettere il prezzo.

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);
}

GridView include un QuantityPerUnit oggetto BoundField, ma questo BoundField deve essere solo a scopo di visualizzazione e non deve essere modificabile dall'utente. Per organizzare questa operazione, è sufficiente impostare la proprietà BoundFields ReadOnly su true.

Impostare QuantityPerUnit BoundField Di sola lettura

Figura 3: Make the BoundField Read-Only (Fare clic per visualizzare l'immagineQuantityPerUnit full-size)

Infine, selezionare la casella di controllo Abilita modifica dal smart tag di GridView. Dopo aver completato questi passaggi, la ErrorHandling.aspx Designer della pagina dovrebbe essere simile alla figura 4.

Rimuovere tutti i campi associati necessari e selezionare la casella di controllo Abilita modifica

Figura 4: Rimuovere tutti i campi associati necessari e selezionare la casella di controllo Abilita modifica (fare clic per visualizzare l'immagine full-size)

A questo punto è disponibile un elenco di tutti i prodotti' ProductName, e campi; tuttavia, solo i ProductNamecampi , UnitPriceUnitPriceQuantityPerUnite UnitsInStockUnitsInStock possono essere modificati.

Gli utenti possono ora modificare facilmente i nomi, i prezzi e le unità dei prodotti nei campi azionari

Figura 5: Gli utenti possono ora modificare facilmente i nomi, i prezzi e le unità nei campi azionari (fare clic per visualizzare l'immagine full-size)

Passaggio 2: Gestione dettagliata delle eccezioni DAL-Level

Anche se GridView modificabile funziona in modo meraviglioso quando gli utenti immettono valori legali per il nome, il prezzo e le unità del prodotto modificato in magazzino, immettendo valori illegali comportano un'eccezione. Ad esempio, l'omettezione del ProductName valore causa la generazione di un'eccezione NoNullAllowedException poiché la ProductName proprietà nella ProductsRow classe ha la proprietà AllowDBNull impostata su false; se il database è inattivo, SqlException verrà generato da TableAdapter quando si tenta di connettersi al database. Senza eseguire alcuna azione, queste eccezioni si bollano dal livello di accesso ai dati al livello di logica di business, quindi alla pagina ASP.NET e infine al runtime di ASP.NET.

A seconda del modo in cui l'applicazione Web è configurata e se si visita o meno l'applicazione da localhost, un'eccezione non gestita può comportare una pagina generica di errore del server, un report di errore dettagliato o una pagina Web descrittiva. Vedere Gestione degli errori dell'applicazione Web in ASP.NET e l'elementocustomErrors per altre informazioni sul modo in cui il runtime di ASP.NET risponde a un'eccezione non rilevata.

La figura 6 mostra la schermata rilevata quando si tenta di aggiornare un prodotto senza specificare il ProductName valore. Questo è il report di errore dettagliato predefinito visualizzato durante l'arrivo tramite localhost.

Omettendo il nome del prodotto verranno visualizzati i dettagli dell'eccezione

Figura 6: Omettendo il nome del prodotto verranno visualizzati i dettagli delle eccezioni (fare clic per visualizzare l'immagine full-size)

Sebbene tali dettagli di eccezione siano utili durante il test di un'applicazione, la presentazione di un utente finale con una schermata di questo tipo a fronte di un'eccezione è minore dell'ideale. Un utente finale probabilmente non conosce cosa NoNullAllowedException è o perché è stato causato. Un approccio migliore consiste nel presentare all'utente un messaggio più descrittivo che spiega che si sono verificati problemi durante il tentativo di aggiornare il prodotto.

Se si verifica un'eccezione durante l'esecuzione dell'operazione, gli eventi post-livello in ObjectDataSource e il controllo Web dati forniscono un mezzo per rilevarlo e annullare l'eccezione da bubbling fino al runtime di ASP.NET. Ad esempio, verrà creato un gestore eventi per l'evento GridView che determina se un'eccezione è stata attivata e, in tal caso, visualizza i dettagli dell'eccezione RowUpdated in un controllo Web Etichetta.

Iniziare aggiungendo un'etichetta alla pagina ASP.NET, impostandone la ID proprietà su ExceptionDetails e cancellandone la Text proprietà. Per disegnare l'occhio dell'utente a questo messaggio, impostare la relativa CssClass proprietà su Warning, che è una classe CSS aggiunta al Styles.css file nell'esercitazione precedente. Tenere presente che questa classe CSS causa la visualizzazione del testo dell'etichetta in un carattere rosso, corsivo, grassetto, extra large.

Aggiungere un controllo Web etichetta alla pagina

Figura 7: Aggiungere un controllo Web etichetta alla pagina (fare clic per visualizzare l'immagine full-size)

Poiché si vuole che questo controllo Web etichetta sia visibile solo immediatamente dopo che si è verificata un'eccezione, impostare la relativa Visible proprietà su false nel Page_Load gestore eventi:

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

Con questo codice, nella prima pagina visitare e postback successivi il ExceptionDetails controllo avrà la relativa Visible proprietà impostata su false. A fronte di un'eccezione a livello dal-o BLL, che è possibile rilevare nel gestore eventi di GridView, verrà impostata la ExceptionDetails proprietà del RowUpdatedVisible controllo su true. Poiché i gestori eventi di controllo Web si verificano dopo il Page_Load gestore eventi nel ciclo di vita della pagina, verrà visualizzata l'etichetta. Tuttavia, nel postback successivo, il Page_Load gestore eventi restituirà nuovamente la Visible proprietà a false, nascondendolo di nuovo dalla visualizzazione.

Nota

In alternativa, è possibile rimuovere la necessità di impostare la proprietà del controllo in assegnandone Visible la ExceptionDetails proprietà false nella sintassi dichiarativa e disabilitando lo stato di Visible visualizzazione (impostandone la EnableViewState proprietà su false).Page_Load Questo approccio alternativo verrà usato in un'esercitazione futura.

Con il controllo Label aggiunto, il passaggio successivo consiste nel creare il gestore eventi per l'evento RowUpdated GridView. Selezionare GridView nella Designer, passare alla Finestra Proprietà e fare clic sull'icona del fulmine, elencando gli eventi di GridView. Per l'evento RowUpdating GridView è già presente una voce, come è stato creato un gestore eventi per questo evento in precedenza in questa esercitazione. Creare anche un gestore eventi per l'evento RowUpdated .

Creare un gestore eventi per l'evento RowUpdated di GridView

Figura 8: Creare un gestore eventi per l'evento RowUpdated GridView

Nota

È anche possibile creare il gestore eventi tramite gli elenchi a discesa nella parte superiore del file di classe code-behind. Selezionare GridView nell'elenco a discesa a sinistra e l'evento RowUpdated da quello a destra.

La creazione di questo gestore eventi aggiungerà il codice seguente alla classe code-behind della pagina di ASP.NET:

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

Il secondo parametro di input del gestore eventi è un oggetto di tipo GridViewUpdatedEventArgs, con tre proprietà di interesse per la gestione delle eccezioni:

  • Exception riferimento all'eccezione generata; se non è stata generata alcuna eccezione, questa proprietà avrà un valore di null
  • ExceptionHandled valore booleano che indica se l'eccezione è stata gestita nel RowUpdated gestore eventi; se false (impostazione predefinita), l'eccezione viene generata nuovamente, percolando fino al runtime di ASP.NET
  • KeepInEditMode se impostato sulla true riga GridView modificata rimane in modalità di modifica; se false (impostazione predefinita), la riga GridView torna alla modalità di sola lettura.

Il codice, quindi, deve verificare se Exception non nullè , vale a dire che è stata generata un'eccezione durante l'esecuzione dell'operazione. In questo caso, si vuole:

  • Visualizzare un messaggio descrittivo nell'etichetta ExceptionDetails
  • Indicare che l'eccezione è stata gestita
  • Mantenere la riga GridView in modalità di modifica

Questo codice seguente consente di raggiungere questi obiettivi:

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;
    }
}

Questo gestore eventi inizia controllando se e.Exception è null. In caso contrario, la ExceptionDetails proprietà dell'etichetta Visible è impostata su true e la relativa Text proprietà su "Si è verificato un problema durante l'aggiornamento del prodotto". I dettagli dell'eccezione effettiva generata risiedono nella e.Exception proprietà dell'oggetto InnerException . Questa eccezione interna viene esaminata e, se si tratta di un particolare tipo, viene aggiunto un messaggio aggiuntivo utile alla ExceptionDetails proprietà dell'etichetta Text . Infine, le ExceptionHandled proprietà e KeepInEditMode sono entrambe impostate su true.

La figura 9 mostra una schermata di questa pagina quando si omette il nome del prodotto; La figura 10 mostra i risultati quando si immette un valore non valido UnitPrice (-50).

ProductName BoundField deve contenere un valore

Figura 9: BoundField ProductName deve contenere un valore (fare clic per visualizzare l'immagine a dimensione intera)

I valori unitPrice negativi non sono consentiti

Figura 10: I valori negativi UnitPrice non sono consentiti (fare clic per visualizzare l'immagine a dimensione intera)

Impostando la e.ExceptionHandled proprietà su true, il RowUpdated gestore eventi ha indicato che ha gestito l'eccezione. Di conseguenza, l'eccezione non verrà propagata fino al runtime di ASP.NET.

Nota

Le figure 9 e 10 mostrano un modo normale per gestire le eccezioni generate a causa di un input utente non valido. Idealmente, tuttavia, tale input non valido non raggiungerà mai il livello della logica di business, perché la pagina ASP.NET dovrebbe garantire che gli input dell'utente siano validi prima di richiamare il ProductsBLL metodo della UpdateProduct classe. Nell'esercitazione successiva verrà illustrato come aggiungere controlli di convalida alle interfacce di modifica e inserimento per assicurarsi che i dati inviati al livello della logica di business siano conformi alle regole business. I controlli di convalida impediscono non solo la chiamata del UpdateProduct metodo fino a quando i dati forniti dall'utente non sono validi, ma forniscono anche un'esperienza utente più informativa per identificare i problemi di immissione dei dati.

Passaggio 3: Gestire correttamente le eccezioni BLL-Level

Durante l'inserimento, l'aggiornamento o l'eliminazione di dati, il livello di accesso ai dati può generare un'eccezione in caso di errore correlato ai dati. Il database potrebbe essere offline, una colonna della tabella di database necessaria potrebbe non avere un valore specificato oppure è possibile che sia stato violato un vincolo a livello di tabella. Oltre alle eccezioni strettamente correlate ai dati, il livello della logica di business può usare le eccezioni per indicare quando le regole business sono state violate. Nell'esercitazione Creazione di un livello di logica di business, ad esempio, è stato aggiunto un controllo della regola business all'overload originaleUpdateProduct. In particolare, se l'utente contrassegnava un prodotto come sospeso, era necessario che il prodotto non sia l'unico fornito dal fornitore. Se questa condizione è stata violata, è stata generata un'eccezione ApplicationException .

Per l'overload UpdateProduct creato in questa esercitazione, aggiungere una regola business che impedisce l'impostazione del UnitPrice campo a un nuovo valore superiore al doppio del valore originale UnitPrice . A tale scopo, modificare l'overload UpdateProduct in modo che esegua questo controllo e generi un'eccezione ApplicationException se la regola viene violata. Il metodo aggiornato segue:

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;
}

Con questa modifica, qualsiasi aggiornamento del prezzo superiore al doppio del prezzo esistente causerà la generazione di un oggetto ApplicationException . Proprio come l'eccezione generata da DAL, questo BLL generato ApplicationException può essere rilevato e gestito nel gestore eventi di RowUpdated GridView. Infatti, il RowUpdated codice del gestore eventi, come scritto, rileverà correttamente questa eccezione e visualizzerà il ApplicationExceptionvalore della proprietà .Message La figura 11 mostra uno screenshot quando un utente tenta di aggiornare il prezzo di Chai a $ 50,00, che è più del doppio del prezzo corrente di $ 19,95.

Le regole di business non consentono l'aumento del prezzo che più del doppio prezzo di un prodotto

Figura 11: Le regole business non consentono aumenti di prezzo che più di doppio prezzo di un prodotto (fare clic per visualizzare l'immagine a dimensione intera)

Nota

Idealmente, le regole della logica di business verrebbero sottoposte a refactoring dagli overload del UpdateProduct metodo e in un metodo comune. Questa operazione viene lasciata come esercizio per il lettore.

Riepilogo

Durante l'inserimento, l'aggiornamento e l'eliminazione di operazioni, sia il controllo Web dei dati che objectDataSource hanno coinvolto eventi pre e post-livello che consentono di modificare l'operazione effettiva. Come illustrato in questa esercitazione e in quella precedente, quando si usa un oggetto GridView modificabile viene generato l'evento gridView, seguito dall'evento RowUpdatingUpdating ObjectDataSource, a cui il comando di aggiornamento viene eseguito all'oggetto sottostante di ObjectDataSource. Al termine dell'operazione, viene generato l'evento ObjectDataSource Updated , seguito dall'evento gridView RowUpdated .

È possibile creare gestori eventi per gli eventi di prelivello per personalizzare i parametri di input o per gli eventi post-livello per controllare e rispondere ai risultati dell'operazione. I gestori eventi post-livello vengono usati più comunemente per rilevare se si è verificata un'eccezione durante l'operazione. In caso di eccezione, questi gestori eventi post-livello possono facoltativamente gestire l'eccezione autonomamente. In questa esercitazione è stato illustrato come gestire tale eccezione visualizzando un messaggio di errore descrittivo.

Nell'esercitazione successiva verrà illustrato come ridurre la probabilità di eccezioni derivanti da problemi di formattazione dei dati, ad esempio l'immissione di un valore negativo UnitPrice. In particolare, si esaminerà come aggiungere controlli di convalida alle interfacce di modifica e inserimento.

Buon programmatori!

Informazioni sull'autore

Scott Mitchell, autore di sette libri ASP/ASP.NET e fondatore di 4GuysFromRolla.com, lavora con le tecnologie Web Microsoft dal 1998. Scott lavora come consulente indipendente, formatore e scrittore. Il suo ultimo libro è Sams Teach Yourself ASP.NET 2.0 in 24 ore. Può essere raggiunto all'indirizzo mitchell@4GuysFromRolla.com. o tramite il suo blog, disponibile all'indirizzo http://ScottOnWriting.NET.

Grazie speciale a

Questa serie di esercitazioni è stata esaminata da molti revisori utili. Il revisore principale per questa esercitazione è stato Liz Shulok. Si è interessati a esaminare i prossimi articoli MSDN? In tal caso, rilasciami una riga in mitchell@4GuysFromRolla.com.