Sdílet prostřednictvím


Zpracování výjimek na úrovni knihoven BLL a DAL na stránce ASP.NET (C#)

Scott Mitchell

Stáhnout PDF

V tomto kurzu se dozvíte, jak zobrazit popisnou a informativní chybovou zprávu, pokud během operace vložení, aktualizace nebo odstranění webového ovládacího prvku ASP.NET dat dojde k výjimce.

Úvod

Práce s daty z ASP.NET webové aplikace pomocí architektury vrstvených aplikací zahrnuje následující tři obecné kroky:

  1. Určete, jakou metodu vrstvy obchodní logiky je potřeba vyvolat a jaké hodnoty parametrů se mají předat. Hodnoty parametrů můžou být pevně kódované, programově přiřazené nebo vstupy zadané uživatelem.
  2. Vyvolejte metodu .
  3. Zpracujte výsledky. Při volání metody BLL, která vrací data, to může zahrnovat vazbu dat na datový webový ovládací prvek. U metod BLL, které upravují data, to může zahrnovat provedení určité akce na základě návratové hodnoty nebo řádné zpracování výjimky, která vznikla v kroku 2.

Jak jsme viděli v předchozím kurzu, ovládací prvky ObjectDataSource a data Web poskytují body rozšiřitelnosti pro kroky 1 a 3. Objekt GridView například aktivuje událost RowUpdating před přiřazením hodnot svých polí kolekci ObjectDataSource UpdateParameters ; jeho RowUpdated událost je vyvolána po dokončení operace ObjectDataSource.

Už jsme prozkoumali události, které se aktivují během kroku 1, a zjistili jsme, jak je možné je použít k přizpůsobení vstupních parametrů nebo zrušení operace. V tomto kurzu obrátíme pozornost na události, které se aktivují po dokončení operace. Pomocí těchto obslužných rutin událostí po úrovni můžeme mimo jiné určit, jestli během operace došlo k výjimce, a elegantně ji zpracovat a zobrazit na obrazovce popisnou a informativní chybovou zprávu místo výchozího nastavení standardní stránky ASP.NET výjimky.

Pro ilustraci práce s těmito událostmi po úrovni vytvoříme stránku se seznamem produktů v upravitelném zobrazení GridView. Pokud při aktualizaci produktu dojde k výjimce, zobrazí se na stránce ASP.NET nad objektem GridView krátká zpráva s vysvětlením, že došlo k problému. Pusťme se do toho.

Krok 1: Vytvoření upravitelného zobrazení GridView produktů

V předchozím kurzu jsme vytvořili upravitelný objekt GridView se dvěma poli ProductName a UnitPrice. To vyžadovalo vytvoření dalšího přetížení pro metodu ProductsBLL třídy UpdateProduct , která přijímala pouze tři vstupní parametry (název produktu, jednotkovou cenu a ID) na rozdíl od parametru pro každé pole produktu. Pro účely tohoto kurzu si tuto techniku znovu procvičme a vytvoříme upravitelný objekt GridView, který zobrazuje název produktu, množství na jednotku, jednotkovou cenu a jednotky na skladě, ale umožňuje úpravy pouze názvu, jednotkové ceny a jednotek na skladě.

Pro tento scénář budeme potřebovat další přetížení UpdateProduct metody, které přijímá čtyři parametry: název produktu, jednotkovou cenu, jednotky na skladě a ID. Do třídy ProductsBLL přidejte následující metodu:

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

Po dokončení této metody jsme připraveni vytvořit stránku ASP.NET, která umožňuje upravit tato čtyři konkrétní pole produktů. ErrorHandling.aspx Otevřete stránku ve EditInsertDelete složce a přidejte gridview na stránku prostřednictvím Designer. Vytvořte vazbu GridView na nový ObjectDataSource a namapujte metodu Select() na metodu ProductsBLL třídy GetProducts() a metodu Update() na UpdateProduct právě vytvořené přetížení.

Použití přetížení metody UpdateProduct, která přijímá čtyři vstupní parametry

Obrázek 1: Použití UpdateProduct přetížení metody, která přijímá čtyři vstupní parametry (kliknutím zobrazíte obrázek v plné velikosti)

Tím se vytvoří ObjectDataSource s kolekcí UpdateParameters se čtyřmi parametry a Objekt GridView s polem pro každé pole produktu. Deklarativní kód ObjectDataSource přiřadí OldValuesParameterFormatString vlastnosti hodnotu original_{0}, která způsobí výjimku, protože naše třída BLL neočekává předání vstupního parametru s názvem original_productID . Nezapomeňte toto nastavení úplně odebrat z deklarativní syntaxe (nebo ho nastavit na výchozí hodnotu {0}).

Dále parepište Objekt GridView tak, aby zahrnoval pouze ProductNameobjekty , QuantityPerUnitUnitPrice, a UnitsInStock BoundFields. Nebojte se také použít jakékoli formátování na úrovni pole, které považujete za nezbytné (například změnu HeaderText vlastností).

V předchozím kurzu jsme se podívali na to, jak formátovat UnitPrice BoundField jako měnu v režimu jen pro čtení i v režimu úprav. Uděláme to samé tady. Vzpomeňte si, že toto nastavení vyžaduje nastavení vlastnosti BoundField DataFormatString na {0:c}, jeho HtmlEncode vlastnost na falsea jeho ApplyFormatInEditMode na true, jak je znázorněno na obrázku 2.

Konfigurace pole UnitPrice BoundField tak, aby se zobrazovala jako měna

Obrázek 2: Konfigurace UnitPrice pole BoundField tak, aby se zobrazilo jako měna (kliknutím zobrazíte obrázek v plné velikosti)

Formátování UnitPrice jako měny v rozhraní pro úpravy vyžaduje vytvoření obslužné rutiny události gridview události RowUpdating , která parsuje řetězec ve formátu měny na decimal hodnotu. Vzpomeňte RowUpdating si, že obslužná rutina události z posledního kurzu také zkontrolovala, aby se zajistilo, že uživatel zadal UnitPrice hodnotu. V tomto kurzu ale umožníme uživateli vynechat cenu.

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

Naše zobrazení GridView obsahuje QuantityPerUnit BoundField, ale toto BoundField by mělo být pouze pro účely zobrazení a uživatel by ho neměl upravovat. Pokud to chcete uspořádat, jednoduše nastavte vlastnost BoundFields ReadOnly na true.

Nastavení pole QuantityPerUnit BoundField jen pro čtení

Obrázek 3: Nastavení QuantityPerUnit Read-Only BoundField (kliknutím zobrazíte obrázek v plné velikosti)

Nakonec zaškrtněte políčko Povolit úpravy u inteligentní značky GridView. Po dokončení těchto kroků ErrorHandling.aspx by Designer stránky měly vypadat podobně jako na obrázku 4.

Odeberte všechny objekty BoundField kromě potřebných a zaškrtněte políčko Povolit úpravy.

Obrázek 4: Odeberte všechna kromě potřebných boundfields a zaškrtněte políčko Povolit úpravy (kliknutím zobrazíte obrázek v plné velikosti).

V tomto okamžiku máme seznam všech polí produktů , , , a UnitsInStock . Upravit se ale dají jenom ProductNamepole , UnitPricea UnitsInStock . UnitPriceQuantityPerUnitProductName

Uživatelé teď můžou snadno upravovat názvy produktů, ceny a jednotky na skladě.

Obrázek 5: Uživatelé teď můžou snadno upravovat názvy produktů, ceny a jednotky v polích zásob (kliknutím zobrazíte obrázek v plné velikosti)

Krok 2: Řádné zpracování DAL-Level výjimek

Zatímco naše upravitelné GridView funguje skvěle, když uživatelé zadají právní hodnoty pro název, cenu a jednotky upraveného produktu na skladě, zadání neplatných hodnot vede k výjimce. Například vynechání ProductName hodnoty způsobí, že NoNullAllowedException bude vyvolána, protože ProductName vlastnost ve ProductsRow třídě má svou AllowDBNull vlastnost nastavenou na falsehodnotu . Pokud je databáze mimo provoz, SqlException bude vyvolána objektEm TableAdapter při pokusu o připojení k databázi. Bez provedení jakékoli akce se tyto výjimky zobrazí z vrstvy přístupu k datům do vrstvy obchodní logiky, pak na stránku ASP.NET a nakonec do ASP.NET modulu runtime.

V závislosti na tom, jak je vaše webová aplikace nakonfigurovaná a jestli aplikaci navštěvujete z localhostaplikace , může neošetřená výjimka vést buď k obecné chybové stránce serveru, podrobné zprávě o chybách, nebo k uživatelsky přívětivé webové stránce. Další informace o tom, jak modul runtime ASP.NET reaguje na nezachycenou výjimku, najdete v tématech Zpracování chyb webové aplikace v ASP.NET a elementu customErrors .

Obrázek 6 znázorňuje obrazovku zobrazenou při pokusu o aktualizaci produktu bez zadání ProductName hodnoty. Toto je výchozí podrobná zpráva o chybách, která se zobrazí při procházení .localhost

Při vynechání názvu produktu se zobrazí podrobnosti o výjimce.

Obrázek 6: Vynechání názvu produktu zobrazí podrobnosti o výjimce (kliknutím zobrazíte obrázek v plné velikosti)

I když jsou takové podrobnosti o výjimce užitečné při testování aplikace, prezentace takové obrazovky koncovému uživateli před výjimkou není ideální. Koncový uživatel pravděpodobně neví, co NoNullAllowedException je nebo proč k němu došlo. Lepším přístupem je zobrazit uživateli uživatelsky přívětivější zprávu vysvětlující, že při pokusu o aktualizaci produktu došlo k problémům.

Pokud při provádění operace dojde k výjimce, události po úrovni v ovládacím prvku ObjectDataSource a data Web poskytují prostředky k jeho zjištění a zrušení výjimky z probublávání až do ASP.NET modulu runtime. V našem příkladu vytvoříme obslužnou rutinu události objektu GridView RowUpdated , která určí, jestli se aktivovala výjimka, a pokud ano, zobrazí podrobnosti o výjimce v ovládacím prvku Label Web.

Začněte tím, že na stránku ASP.NET přidáte popisek, nastavíte jeho ID vlastnost na ExceptionDetails a vymažete jeho Text vlastnost. Pokud chcete upoutat pozornost uživatele na tuto zprávu, nastavte jeho CssClass vlastnost na Warning, což je třída CSS, kterou jsme přidali do Styles.css souboru v předchozím kurzu. Vzpomeňte si, že tato třída CSS způsobí, že se text popisku zobrazí červeným, kurzívou, tučným a extra velkým písmem.

Přidání webového ovládacího prvku Label na stránku

Obrázek 7: Přidání webového ovládacího prvku Popisek na stránku (kliknutím zobrazíte obrázek v plné velikosti)

Vzhledem k tomu, že chceme, aby byl tento ovládací prvek Label Web viditelný hned po výjimce, nastavte jeho Visible vlastnost v obslužné rutině události na Page_Load hodnotu false:

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

S tímto kódem bude mít ovládací prvek při první návštěvě stránky a následných zpětných ExceptionDetails odesláních nastavenou vlastnost Visible na false. V případě výjimky na úrovni DAL nebo BLL, kterou můžeme zjistit v obslužné rutině události GridView RowUpdated , nastavíme ExceptionDetails vlastnost ovládacího prvku Visible na true. Vzhledem k tomu, že se obslužné rutiny událostí webového Page_Load ovládacího prvku vyskytují po obslužné rutině události v životním cyklu stránky, zobrazí se popisek. Při dalším zpětném odeslání Page_Load však obslužná rutina události vrátí Visible vlastnost zpět na falsea znovu ji skryje ze zobrazení.

Poznámka

Alternativně bychom mohli odebrat nutnost nastavení ExceptionDetails vlastnosti ovládacího prvku Visible v Page_Load souboru tím, že přiřadíme jeho Visible vlastnost false v deklarativní syntaxi a zakážeme jeho stav zobrazení (nastavíme jeho EnableViewState vlastnost na false). Tento alternativní přístup použijeme v budoucím kurzu.

Po přidání ovládacího prvku Label je naším dalším krokem vytvoření obslužné rutiny události pro událost GridView RowUpdated . V Designer vyberte Objekt GridView, přejděte na okno Vlastnosti a klikněte na ikonu blesku se seznamem událostí objektu GridView. Už by tam měla být položka pro událost GridView RowUpdating , protože jsme pro tuto událost dříve v tomto kurzu vytvořili obslužnou rutinu události. Vytvořte také obslužnou rutinu RowUpdated události pro událost.

Vytvoření obslužné rutiny události pro událost RowUpdated objektu GridView

Obrázek 8: Vytvoření obslužné rutiny události pro událost GridView RowUpdated

Poznámka

Obslužnou rutinu události můžete také vytvořit prostřednictvím rozevíracích seznamů v horní části souboru třídy kódu na pozadí. V rozevíracím seznamu vlevo vyberte GridView a RowUpdated událost z pravého seznamu.

Při vytváření této obslužné rutiny události se do třídy kódu na pozadí stránky ASP.NET přidá následující kód:

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

Druhý vstupní parametr této obslužné rutiny události je objekt typu GridViewUpdatedEventArgs, který má tři vlastnosti, které jsou zajímavé pro zpracování výjimek:

  • Exception odkaz na vyvolanou výjimku; Pokud nebyla vyvolána žádná výjimka, bude mít tato vlastnost hodnotu null
  • ExceptionHandled Logická hodnota, která označuje, jestli byla výjimka zpracována v RowUpdated obslužné rutině události. Pokud false (výchozí), výjimka se znovu vyvolá a přejde až do ASP.NET modulu runtime.
  • KeepInEditMode pokud je nastaveno na true upravený řádek GridView zůstane v režimu úprav. Pokud false (výchozí), řádek GridView se vrátí do režimu jen pro čtení.

Náš kód by pak měl zkontrolovat, jestli Exception není null, což znamená, že při provádění operace byla vyvolána výjimka. V takovém případě chceme:

  • Zobrazení uživatelsky přívětivé zprávy v popisku ExceptionDetails
  • Označení, že výjimka byla zpracována
  • Zachování řádku GridView v režimu úprav

Následující kód dosahuje těchto cílů:

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

Tato obslužná rutina události začíná kontrolou, jestli e.Exception je null. Pokud tomu tak není, ExceptionDetails vlastnost Label je Visible nastavená na true a vlastnost Text "Při aktualizaci produktu došlo k potížím". Podrobnosti o skutečné výjimce, která byla vyvolán, se nacházejí ve vlastnosti objektu e.ExceptionInnerException . Tato vnitřní výjimka se prověří a pokud je konkrétního typu, připojí se k ExceptionDetails vlastnosti Label Text další užitečná zpráva. ExceptionHandled Nakonec jsou vlastnosti a KeepInEditMode nastaveny na truehodnotu .

Obrázek 9 ukazuje snímek obrazovky této stránky při vynechání názvu produktu; Obrázek 10 ukazuje výsledky při zadávání neplatné UnitPrice hodnoty (-50).

ProductName BoundField musí obsahovat hodnotu.

Obrázek 9: BoundField ProductName musí obsahovat hodnotu (kliknutím zobrazíte obrázek v plné velikosti)

Záporné hodnoty UnitPrice nejsou povoleny.

Obrázek 10: Záporné UnitPrice hodnoty nejsou povolené (kliknutím zobrazíte obrázek v plné velikosti)

Nastavením e.ExceptionHandled vlastnosti na truehodnotu byla obslužná RowUpdated rutina události označena, že zpracovala výjimku. Proto se výjimka nebude šířit až do modulu runtime ASP.NET.

Poznámka

Obrázky 9 a 10 ukazují elegantní způsob zpracování výjimek vyvolaných kvůli neplatnému vstupu uživatele. V ideálním případě se ale takový neplatný vstup nikdy nedostane do vrstvy obchodní logiky, protože stránka ASP.NET by měla před vyvoláním ProductsBLL metody třídy UpdateProduct zajistit, aby vstupy uživatele byly platné. V dalším kurzu se dozvíte, jak přidat ověřovací ovládací prvky do rozhraní pro úpravy a vkládání, abychom zajistili, že data odeslaná do vrstvy obchodní logiky odpovídají obchodním pravidlům. Ověřovací ovládací prvky nejen brání vyvolání UpdateProduct metody, dokud nebudou data zadaná uživatelem platná, ale také poskytují informativnější uživatelské prostředí pro identifikaci problémů se zadáváním dat.

Krok 3: Řádné zpracování výjimek BLL-Level

Při vkládání, aktualizaci nebo odstraňování dat může vrstva přístupu k datům vyvolat výjimku tváří v tvář chybě související s daty. Databáze může být offline, požadovaný sloupec tabulky databáze nemusí mít zadanou hodnotu nebo došlo k porušení omezení na úrovni tabulky. Kromě výjimek výhradně souvisejících s daty může vrstva obchodní logiky používat výjimky k označení, kdy došlo k porušení obchodních pravidel. Například v kurzu Vytvoření vrstvy obchodní logiky jsme do původního UpdateProduct přetížení přidali kontrolu obchodních pravidel. Konkrétně pokud uživatel označoval produkt jako ukončený, požadovali jsme, aby produkt nebyl jediný, který mu poskytl jeho dodavatel. Pokud byla tato podmínka porušena, došlo k ApplicationException vyvolání.

UpdateProduct Pro přetížení vytvořené v tomto kurzu přidáme obchodní pravidlo, které zakážeUnitPrice, aby pole bylo nastaveno na novou hodnotu, která je více než dvakrát vyšší než původní UnitPrice hodnota. Chcete-li toho dosáhnout, upravte UpdateProduct přetížení tak, aby provedlo tuto kontrolu a vyvolá chybu ApplicationException , pokud je pravidlo porušeno. Aktualizovaná metoda následuje:

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

Při této změně způsobí ApplicationException jakákoli aktualizace cen, která je více než dvojnásobek stávající ceny, vyvolá vyvolání. Stejně jako výjimka vyvolaná z DAL, může být tato zvýšená ApplicationException BLL rozpoznána a zpracována v obslužné rutině události GridView RowUpdated . Ve skutečnosti RowUpdated kód obslužné rutiny události, jak je napsaný, tuto výjimku správně rozpozná a zobrazí ApplicationExceptionhodnotu vlastnosti .Message Obrázek 11 ukazuje snímek obrazovky, když se uživatel pokusí aktualizovat cenu Chai na 50,00 USD, což je více než dvojnásobek aktuální ceny 19,95 USD.

Obchodní pravidla neumožňují zvýšení cen, které více než zdvojnásobí cenu produktu.

Obrázek 11: Obchodní pravidla neumožňují zvýšení ceny, které je vyšší než dvojnásobek ceny produktu (kliknutím zobrazíte obrázek v plné velikosti)

Poznámka

V ideálním případě by se pravidla obchodní logiky refaktorovala z UpdateProduct přetížení metod do běžné metody. Toto je ponecháno jako cvičení pro čtenáře.

Souhrn

Při vkládání, aktualizaci a odstraňování operací zahrnoval ovládací prvek web dat i ObjectDataSource události před a po úrovni, které zarezervují skutečnou operaci. Jak jsme viděli v tomto kurzu a předchozím, při práci s upravitelným objektem GridView se aktivuje událost GridView RowUpdating následovaná událostí ObjectDataSource Updating , v tomto okamžiku se provede příkaz update na podkladový objekt ObjectDataSource. Po dokončení operace se aktivuje událost ObjectDataSource Updated následovaná událostí GridView RowUpdated .

Můžeme vytvořit obslužné rutiny událostí pro události před úrovní, abychom mohli přizpůsobit vstupní parametry nebo pro události po úrovni, abychom mohli zkontrolovat výsledky operace a reagovat na ně. Obslužné rutiny událostí po úrovni se nejčastěji používají ke zjištění, jestli během operace došlo k výjimce. V případě výjimky můžou tyto obslužné rutiny událostí po úrovni volitelně zpracovávat výjimku samostatně. V tomto kurzu jsme viděli, jak takovou výjimku zpracovat zobrazením popisné chybové zprávy.

V dalším kurzu se dozvíte, jak zmenšit pravděpodobnost výjimek vyplývajících z problémů s formátováním dat (například zadání záporné UnitPricehodnoty ). Konkrétně se podíváme na to, jak přidat ověřovací ovládací prvky do rozhraní pro úpravy a vkládání.

Šťastné programování!

O autorovi

Scott Mitchell, autor sedmi knih o ASP/ASP.NET a zakladatel 4GuysFromRolla.com, pracuje s webovými technologiemi Microsoftu od roku 1998. Scott pracuje jako nezávislý konzultant, školitel a spisovatel. Jeho nejnovější kniha je Sams Teach Yourself ASP.NET 2.0 in 24 Hours. Můžete ho najít na mitchell@4GuysFromRolla.comadrese . nebo prostřednictvím jeho blogu, který najdete na http://ScottOnWriting.NETadrese .

Zvláštní poděkování

Tato série kurzů byla zkontrolována mnoha užitečnými recenzenty. Hlavní kontrolorka tohoto kurzu byla Liz Shulok. Chcete si projít moje nadcházející články na WEBU MSDN? Pokud ano, dejte mi čáru na mitchell@4GuysFromRolla.comadresu .