Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
V tomto kurzu se dozvíte, jak centralizovat obchodní pravidla do vrstvy obchodní logiky (BLL), která slouží jako zprostředkovatel pro výměnu dat mezi prezentační vrstvou a DAL.
Úvod
Vrstva přístupu k datům (DAL) vytvořená v prvním kurzu čistě odděluje logiku přístupu k datům od logiky prezentace. Ačkoli DAL čistě odděluje podrobnosti o přístupu k datům od prezentační vrstvy, nevynucuje žádná obchodní pravidla, která se mohou vztahovat. Pro naši aplikaci můžeme například chtít zakázat úpravu polí CategoryID nebo SupplierID v tabulce Products v případě, že pole Discontinued je nastaveno na hodnotu 1, nebo můžeme chtít vynutit pravidla seniority, zakazující situaci, kdy je zaměstnanec spravován někým, kdo byl přijat později. Dalším běžným scénářem je autorizace, například pouze uživatelé v určité roli můžou odstranit produkty nebo změnit UnitPrice hodnotu.
V tomto kurzu se dozvíte, jak tato obchodní pravidla centralizovat do vrstvy obchodní logiky (BLL), která slouží jako zprostředkovatel pro výměnu dat mezi prezentační vrstvou a DAL. V reálné aplikaci by se BLL mělo implementovat jako samostatný projekt knihovny tříd; Pro tyto kurzy však implementujeme BLL jako řadu tříd v naší App_Code složce, abychom zjednodušili strukturu projektu. Obrázek 1 znázorňuje vztahy architektury mezi prezentační vrstvou, BLL a DAL.
Obrázek 1: BLL odděluje prezentační vrstvu od vrstvy přístupu k datům a ukládá obchodní pravidla.
Krok 1: Vytvoření tříd BLL
Naše BLL bude složena ze čtyř tříd, jedna pro každý TableAdapter v DAL; každá z těchto tříd BLL bude obsahovat metody pro načítání, vkládání, aktualizaci a odstraňování z příslušného TableAdapteru v DAL, s aplikováním příslušných obchodních pravidel.
Pro oddělení tříd souvisejících s DAL a BLL vytvoříme ve složce App_Code dvě podsložky, DAL a BLL. Jednoduše klikněte pravým tlačítkem myši na App_Code složku v Průzkumníku řešení a zvolte Nová složka. Po vytvoření těchto dvou složek přesuňte Typed DataSet vytvořený v prvním kurzu do DAL podsložky.
Dále vytvořte čtyři soubory třídy BLL v BLL podsložce. Provedete to tak, že kliknete pravým tlačítkem na BLL podsložku, zvolíte Přidat novou položku a zvolíte šablonu Třídy. Pojmenujte čtyři třídy ProductsBLL, CategoriesBLL, SuppliersBLLa EmployeesBLL.
Obrázek 2: Přidání čtyř nových tříd do App_Code složky
V dalším kroku přidáme metody do každé třídy, které jednoduše zabalí metody definované pro TableAdaptery z prvního kurzu. Prozatím tyto metody budou jen volat přímo do DAL; Později se vrátíme, abychom přidali jakoukoli potřebnou obchodní logiku.
Poznámka:
Pokud používáte Visual Studio Standard Edition nebo vyšší (to znamená, že nepoužíváte Visual Web Developer), můžete volitelně navrhnout třídy vizuálně pomocí Návrháře tříd. Další informace o této nové funkci v sadě Visual Studio najdete v blogu Návrháře tříd .
ProductsBLL Pro třídu potřebujeme přidat celkem sedm metod:
-
GetProducts()vrátí všechny produkty. -
GetProductByProductID(productID)vrátí produkt se zadaným ID produktu. -
GetProductsByCategoryID(categoryID)vrátí všechny produkty ze zadané kategorie. -
GetProductsBySupplier(supplierID)vrátí všechny produkty od zadaného dodavatele. -
AddProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued)vloží nový produkt do databáze pomocí předaných hodnot;ProductIDvrátí hodnotu nově vloženého záznamu. -
UpdateProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued, productID)aktualizuje existující produkt v databázi pomocí předaných hodnot; vrátítrue, pokud byl právě jeden řádek aktualizován,falsejinak -
DeleteProduct(productID)odstraní zadaný produkt z databáze.
ProductsBLL.cs
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using NorthwindTableAdapters;
[System.ComponentModel.DataObject]
public class ProductsBLL
{
private ProductsTableAdapter _productsAdapter = null;
protected ProductsTableAdapter Adapter
{
get {
if (_productsAdapter == null)
_productsAdapter = new ProductsTableAdapter();
return _productsAdapter;
}
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Select, true)]
public Northwind.ProductsDataTable GetProducts()
{
return Adapter.GetProducts();
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Select, false)]
public Northwind.ProductsDataTable GetProductByProductID(int productID)
{
return Adapter.GetProductByProductID(productID);
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Select, false)]
public Northwind.ProductsDataTable GetProductsByCategoryID(int categoryID)
{
return Adapter.GetProductsByCategoryID(categoryID);
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Select, false)]
public Northwind.ProductsDataTable GetProductsBySupplierID(int supplierID)
{
return Adapter.GetProductsBySupplierID(supplierID);
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Insert, true)]
public bool AddProduct(string productName, int? supplierID, int? categoryID,
string quantityPerUnit, decimal? unitPrice, short? unitsInStock,
short? unitsOnOrder, short? reorderLevel, bool discontinued)
{
// Create a new ProductRow instance
Northwind.ProductsDataTable products = new Northwind.ProductsDataTable();
Northwind.ProductsRow product = products.NewProductsRow();
product.ProductName = productName;
if (supplierID == null) product.SetSupplierIDNull();
else product.SupplierID = supplierID.Value;
if (categoryID == null) product.SetCategoryIDNull();
else product.CategoryID = categoryID.Value;
if (quantityPerUnit == null) product.SetQuantityPerUnitNull();
else product.QuantityPerUnit = quantityPerUnit;
if (unitPrice == null) product.SetUnitPriceNull();
else product.UnitPrice = unitPrice.Value;
if (unitsInStock == null) product.SetUnitsInStockNull();
else product.UnitsInStock = unitsInStock.Value;
if (unitsOnOrder == null) product.SetUnitsOnOrderNull();
else product.UnitsOnOrder = unitsOnOrder.Value;
if (reorderLevel == null) product.SetReorderLevelNull();
else product.ReorderLevel = reorderLevel.Value;
product.Discontinued = discontinued;
// Add the new product
products.AddProductsRow(product);
int rowsAffected = Adapter.Update(products);
// Return true if precisely one row was inserted,
// otherwise false
return rowsAffected == 1;
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Update, true)]
public bool UpdateProduct(string productName, int? supplierID, int? categoryID,
string quantityPerUnit, decimal? unitPrice, short? unitsInStock,
short? unitsOnOrder, short? reorderLevel, bool discontinued, 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 (supplierID == null) product.SetSupplierIDNull();
else product.SupplierID = supplierID.Value;
if (categoryID == null) product.SetCategoryIDNull();
else product.CategoryID = categoryID.Value;
if (quantityPerUnit == null) product.SetQuantityPerUnitNull();
else product.QuantityPerUnit = quantityPerUnit;
if (unitPrice == null) product.SetUnitPriceNull();
else product.UnitPrice = unitPrice.Value;
if (unitsInStock == null) product.SetUnitsInStockNull();
else product.UnitsInStock = unitsInStock.Value;
if (unitsOnOrder == null) product.SetUnitsOnOrderNull();
else product.UnitsOnOrder = unitsOnOrder.Value;
if (reorderLevel == null) product.SetReorderLevelNull();
else product.ReorderLevel = reorderLevel.Value;
product.Discontinued = discontinued;
// Update the product record
int rowsAffected = Adapter.Update(product);
// Return true if precisely one row was updated,
// otherwise false
return rowsAffected == 1;
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Delete, true)]
public bool DeleteProduct(int productID)
{
int rowsAffected = Adapter.Delete(productID);
// Return true if precisely one row was deleted,
// otherwise false
return rowsAffected == 1;
}
}
Metody, které jednoduše vracejí data GetProducts, GetProductByProductIDGetProductsByCategoryIDa GetProductBySuppliersID jsou poměrně jednoduché, protože jednoduše volají do DAL. V některých scénářích mohou existovat obchodní pravidla, která je potřeba implementovat na této úrovni (například autorizační pravidla založená na aktuálně přihlášeného uživatele nebo roli, do které uživatel patří), tyto metody jednoduše ponecháme as-is. Pro tyto metody pak BLL slouží pouze jako proxy, prostřednictvím kterého prezentační vrstva přistupuje k podkladovým datům z vrstvy přístupu k datům.
Obě metody AddProduct a UpdateProduct přebírají jako parametry hodnoty pro různá pole produktů a buď přidají nový produkt, nebo aktualizují existující. Vzhledem k tomu, že mnoho sloupců tabulky Product může přijímat hodnoty NULL (CategoryID, SupplierID a UnitPrice), abyste jmenovali několik, tyto vstupní parametry pro AddProduct a UpdateProduct, které se mapují na tyto sloupce, používají typy s možností null hodnot. Typy s možnou hodnotou null jsou nové pro .NET 2.0 a umožňují označit, zda má být typ hodnoty null. V jazyce C# můžete typ hodnoty označit jako typ s možnou hodnotou null přidáním ? za typ (například int? x;). Další informace najdete v části Typy s možnou hodnotou Null v Průvodci programováním v C# .
Všechny tři metody vrátí logickou hodnotu označující, zda byl řádek vložen, aktualizován nebo odstraněn, protože operace nemusí mít za následek ovlivněný řádek. Pokud například vývojář stránky volá DeleteProduct předávající neexistující ProductID jako argument, DELETE příkaz vydaný do databáze nebude mít žádný vliv, a proto metoda DeleteProduct vrátí false.
Všimněte si, že když přidáte nový produkt nebo aktualizujeme existující produkt, vezmeme hodnoty polí nového nebo upraveného produktu jako seznam skalárů na rozdíl od přijetí ProductsRow instance. Tento přístup byl zvolen, protože ProductsRow třída je odvozena od třídy ADO.NET DataRow , která nemá výchozí konstruktor bez parametrů. Abychom mohli vytvořit novou ProductsRow instanci, musíme nejprve vytvořit ProductsDataTable instanci a pak vyvolat její NewProductRow() metodu (kterou děláme v AddProduct). Když se obrátíme na vkládání a aktualizaci produktů pomocí ObjectDataSource, tento nedostatek se projevuje. Stručně řečeno, ObjectDataSource se pokusí vytvořit instanci vstupních parametrů. Pokud metoda BLL očekává ProductsRow instanci, ObjectDataSource se pokusí vytvořit jednu, ale selže kvůli nedostatku výchozího konstruktoru bez parametrů. Další informace o tomto problému naleznete v následujících dvou ASP.NET fór příspěvky: Aktualizace ObjectDataSources pomocí Strongly-Typed DataSets a Problém s ObjectDataSource a Strongly-Typed DataSet.
Dále v obou AddProduct a UpdateProduct kód vytvoří instanci ProductsRow a naplní ji hodnotami, které byly právě předány. Při přiřazování hodnot do DataColumnů DataRowu může dojít ke různým kontrolám ověření na úrovni polí. Proto ruční vložení předaných hodnot zpět do DataRow pomáhá zajistit platnost dat předávaných metodě BLL. Bohužel, silně typizované třídy DataRow vygenerované sadou Visual Studio nepoužívají nulovatelné typy. Chcete-li označit, že konkrétní DataColumn v DataRow by měl odpovídat hodnotě databáze NULL, musíme použít metodu SetColumnNameNull().
Nejprve UpdateProduct načteme produkt, který se má aktualizovat pomocí GetProductByProductID(productID). I když to může vypadat jako nepotřebná cesta do databáze, ukáže se tato další cesta jako užitečná v budoucích kurzech, které prozkoumají optimistickou souběžnost. Optimistická konkurenční technika zajistí, aby dva uživatelé, kteří současně pracují na stejných datech, náhodou nezaměnili změny toho druhého. Získání celého záznamu také usnadňuje vytváření aktualizačních metod v BLL, které upravují pouze podmnožinu sloupců DataRow. Když prozkoumáme SuppliersBLL třídu, uvidíme takový příklad.
Nakonec mějte na ProductsBLL paměti, že třída má použitý atribut DataObject (syntax [System.ComponentModel.DataObject] přímo před příkazem třídy v horní části souboru) a metody mají atributy DataObjectMethodAttribute. Atribut DataObject označuje třídu jako objekt vhodný pro vazbu na ObjectDataSource ovládacího prvku, zatímco DataObjectMethodAttribute označuje účel metody. Jak uvidíme v budoucích kurzech, ASP.NET ObjectDataSource 2.0 usnadňuje deklarativní přístup k datům z třídy. Chcete-li pomoci filtrovat seznam možných tříd, které se mají svázat v průvodci ObjectDataSource, ve výchozím nastavení jsou v rozevíracím seznamu průvodce zobrazeny pouze ty třídy, které jsou označeny jako DataObjects. Třída ProductsBLL bude fungovat i bez těchto atributů, ale jejich přidání usnadňuje práci v průvodci ObjectDataSource.
Přidání dalších tříd
S dokončenou ProductsBLL třídou stále potřebujeme přidat třídy pro práci s kategoriemi, dodavateli a zaměstnanci. Chvíli si vytvořte následující třídy a metody s využitím konceptů z výše uvedeného příkladu:
CategoriesBLL.cs
GetCategories()GetCategoryByCategoryID(categoryID)
SuppliersBLL.cs
GetSuppliers()GetSupplierBySupplierID(supplierID)GetSuppliersByCountry(country)UpdateSupplierAddress(supplierID, address, city, country)
EmployeesBLL.cs
GetEmployees()GetEmployeeByEmployeeID(employeeID)GetEmployeesByManager(managerID)
Za zmínku stojí metoda SuppliersBLL třídy UpdateSupplierAddress . Tato metoda poskytuje rozhraní pro aktualizaci pouze informací o adrese dodavatele. Interně tato metoda čte objekt SupplierDataRow pro zadané supplierID (používá se GetSupplierBySupplierID), nastaví jeho vlastnosti související s adresou a pak volá ke SupplierDataTable metodě objektu Update. Metoda UpdateSupplierAddress následuje:
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Update, true)]
public bool UpdateSupplierAddress
(int supplierID, string address, string city, string country)
{
Northwind.SuppliersDataTable suppliers =
Adapter.GetSupplierBySupplierID(supplierID);
if (suppliers.Count == 0)
// no matching record found, return false
return false;
else
{
Northwind.SuppliersRow supplier = suppliers[0];
if (address == null) supplier.SetAddressNull();
else supplier.Address = address;
if (city == null) supplier.SetCityNull();
else supplier.City = city;
if (country == null) supplier.SetCountryNull();
else supplier.Country = country;
// Update the supplier Address-related information
int rowsAffected = Adapter.Update(supplier);
// Return true if precisely one row was updated,
// otherwise false
return rowsAffected == 1;
}
}
V tomto článku si můžete stáhnout kompletní implementaci tříd BLL.
Krok 2: Přístup k typed dataSets prostřednictvím tříd BLL
V prvním kurzu jsme viděli příklady práce přímo s typed DataSet programově, ale s přidáním našich tříd BLL by prezentační vrstva měla místo toho pracovat s BLL. V příkladu AllProducts.aspx z prvního kurzu ProductsTableAdapter se použilo k vytvoření vazby seznamu produktů k Objektu GridView, jak je znázorněno v následujícím kódu:
ProductsTableAdapter productsAdapter = new ProductsTableAdapter();
GridView1.DataSource = productsAdapter.GetProducts();
GridView1.DataBind();
Chcete-li použít nové třídy BLL, vše, co je třeba změnit, je první řádek kódu jednoduše nahradit ProductsTableAdapter objekt objektem ProductBLL :
ProductsBLL productLogic = new ProductsBLL();
GridView1.DataSource = productLogic.GetProducts();
GridView1.DataBind();
Třídy BLL lze také přistupovat deklarativním způsobem (stejně jako Typed DataSet) pomocí ObjectDataSource. Podrobněji probereme ObjectDataSource v následujících kurzech.
Obrázek 3: Seznam produktů se zobrazí v objektu GridView (kliknutím zobrazíte obrázek s plnou velikostí).
Krok 3: Přidání ověřování Field-Level do tříd DataRow
Ověřování na úrovni pole jsou kontroly, které se týkají hodnot vlastností obchodních objektů při vkládání nebo aktualizaci. Mezi ověřovací pravidla na úrovni pole pro produkty patří:
- Pole
ProductNamemusí mít délku 40 znaků nebo méně. - Pole
QuantityPerUnitmusí mít délku 20 znaků nebo méně. - Pole
ProductIDaProductNamepoleDiscontinuedjsou povinná, ale všechna ostatní pole jsou volitelná. - Pole
UnitPrice,UnitsInStock,UnitsOnOrderaReorderLevelmusí být větší nebo rovna nule.
Tato pravidla můžou a měla by být vyjádřena na úrovni databáze. Omezení znaků pro pole ProductName a QuantityPerUnit pole jsou zachyceny datovými typy těchto sloupců v Products tabulce (nvarchar(40) a nvarchar(20)v uvedeném pořadí). Určuje, jestli jsou pole povinná a nepovinná, pokud sloupec tabulky databáze umožňuje NULL . Existují čtyři omezení kontroly , která zajišťují, že pouze hodnoty větší než nebo rovny nule můžou být v objektu UnitPrice, UnitsInStock, UnitsOnOrdernebo ReorderLevel ve sloupcích.
Kromě vynucování těchto pravidel v databázi by se měla vynucovat také na úrovni datové sady. Délka pole a to, jestli je hodnota povinná nebo nepovinná, jsou ve skutečnosti zaznamenány pro každou sadu DataColumns datové tabulky. Pokud chcete zobrazit existující ověřování na úrovni pole automaticky, přejděte do Návrháře datové sady, vyberte pole z jedné z datových tabulek a pak přejděte do okna Vlastnosti. Jak je znázorněno na obrázku 4, QuantityPerUnit DataColumn v ProductsDataTable má maximální délku 20 znaků a umožňuje NULL hodnoty. Pokud se pokusíme nastavit vlastnost ProductsDataRowQuantityPerUnit na řetězcovou hodnotu delší než 20 znaků, bude vyvolána chyba ArgumentException.
Obrázek 4: Datový sloupec poskytuje základní ověřování Field-Level (kliknutím zobrazíte obrázek v plné velikosti).
Bohužel nemůžeme určit kontroly hranic, například UnitPrice hodnota musí být větší nebo rovna nule, prostřednictvím okna Vlastnosti. Abychom mohli poskytnout tento typ ověřování na úrovni pole, potřebujeme vytvořit obslužnou rutinu události pro událost ColumnChanging tabulky DataTable. Jak je uvedeno v předchozím kurzu, objekty DataSet, DataTables a DataRow vytvořené Typed DataSet lze rozšířit pomocí částečných tříd. Pomocí této techniky můžeme vytvořit obslužnou rutinu události ColumnChanging pro třídu ProductsDataTable. Začněte vytvořením třídy ve App_Code složce s názvem ProductsDataTable.ColumnChanging.cs.
Obrázek 5: Přidání nové třídy do App_Code složky (kliknutím zobrazíte obrázek v plné velikosti)
Dále vytvořte obslužnou rutinu pro událost ColumnChanging, která zajišťuje, že hodnoty sloupců UnitPrice, UnitsInStock, UnitsOnOrder, a ReorderLevel (pokud ne NULL) jsou větší nebo rovny nule. V případě, že je některý takový sloupec mimo rozsah, vyvolejte výjimku ArgumentException.
ProductsDataTable.ColumnChanging.cs
public partial class Northwind
{
public partial class ProductsDataTable
{
public override void BeginInit()
{
this.ColumnChanging += ValidateColumn;
}
void ValidateColumn(object sender,
DataColumnChangeEventArgs e)
{
if(e.Column.Equals(this.UnitPriceColumn))
{
if(!Convert.IsDBNull(e.ProposedValue) &&
(decimal)e.ProposedValue < 0)
{
throw new ArgumentException(
"UnitPrice cannot be less than zero", "UnitPrice");
}
}
else if (e.Column.Equals(this.UnitsInStockColumn) ||
e.Column.Equals(this.UnitsOnOrderColumn) ||
e.Column.Equals(this.ReorderLevelColumn))
{
if (!Convert.IsDBNull(e.ProposedValue) &&
(short)e.ProposedValue < 0)
{
throw new ArgumentException(string.Format(
"{0} cannot be less than zero", e.Column.ColumnName),
e.Column.ColumnName);
}
}
}
}
}
Krok 4: Přidání vlastních obchodních pravidel do tříd podnikové logiky (BLL)
Kromě ověřování na úrovni polí můžou existovat vlastní obchodní pravidla vysoké úrovně, která zahrnují různé entity nebo koncepty, které se nedají vyjádřit na úrovni jednoho sloupce, například:
- Pokud je produkt ukončený, nejde ho
UnitPriceaktualizovat. - Země bydliště zaměstnance musí být stejná jako země bydliště svého manažera.
- Produkt nelze ukončit, pokud je jediným produktem poskytovaným dodavatelem.
Třídy BLL by měly obsahovat kontroly, které zajistí dodržování obchodních pravidel aplikace. Tyto kontroly lze přidat přímo do metod, na které se vztahují.
Představte si, že naše obchodní pravidla určují, že produkt nemůže být označen jako ukončený, pokud je jediným produktem od daného dodavatele. To znamená, že pokud byl produkt X jediným produktem, který jsme koupili od dodavatele Y, nemohli jsme označit X jako ukončený; pokud nám ale dodavatel Y poskytl tři produkty, A, B a C, mohli bychom všechny tyto produkty označit jako ukončené. Liché obchodní pravidlo, ale obchodní pravidla a zdravý rozum nejsou vždy sladěné!
Abychom toto obchodní pravidlo vynutili v metodě UpdateProducts, začneme kontrolou, zda je Discontinued nastaveno na true, a pokud ano, zavoláme GetProductsBySupplierID, abychom zjistili, kolik produktů jsme zakoupili od dodavatele tohoto produktu. Pokud je od tohoto dodavatele zakoupen pouze jeden produkt, vyhodíme ApplicationException.
public bool UpdateProduct(string productName, int? supplierID, int? categoryID,
string quantityPerUnit, decimal? unitPrice, short? unitsInStock,
short? unitsOnOrder, short? reorderLevel, bool discontinued, 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];
// Business rule check - cannot discontinue
// a product that is supplied by only
// one supplier
if (discontinued)
{
// Get the products we buy from this supplier
Northwind.ProductsDataTable productsBySupplier =
Adapter.GetProductsBySupplierID(product.SupplierID);
if (productsBySupplier.Count == 1)
// this is the only product we buy from this supplier
throw new ApplicationException(
"You cannot mark a product as discontinued if it is the only
product purchased from a supplier");
}
product.ProductName = productName;
if (supplierID == null) product.SetSupplierIDNull();
else product.SupplierID = supplierID.Value;
if (categoryID == null) product.SetCategoryIDNull();
else product.CategoryID = categoryID.Value;
if (quantityPerUnit == null) product.SetQuantityPerUnitNull();
else product.QuantityPerUnit = quantityPerUnit;
if (unitPrice == null) product.SetUnitPriceNull();
else product.UnitPrice = unitPrice.Value;
if (unitsInStock == null) product.SetUnitsInStockNull();
else product.UnitsInStock = unitsInStock.Value;
if (unitsOnOrder == null) product.SetUnitsOnOrderNull();
else product.UnitsOnOrder = unitsOnOrder.Value;
if (reorderLevel == null) product.SetReorderLevelNull();
else product.ReorderLevel = reorderLevel.Value;
product.Discontinued = discontinued;
// Update the product record
int rowsAffected = Adapter.Update(product);
// Return true if precisely one row was updated,
// otherwise false
return rowsAffected == 1;
}
Reakce na chyby ověřování v prezentační vrstvě
Při volání BLL z prezentační vrstvy můžeme rozhodnout, zda se pokusíme zpracovat jakékoli výjimky, které by mohly být vyvolány, nebo je necháme propagovat do ASP.NET (což vyvolá událost HttpApplicationError). Pokud chcete zpracovat výjimku při práci s BLL programově, můžeme použít try...catch blok, jak ukazuje následující příklad:
ProductsBLL productLogic = new ProductsBLL();
// Update information for ProductID 1
try
{
// This will fail since we are attempting to use a
// UnitPrice value less than 0.
productLogic.UpdateProduct(
"Scott s Tea", 1, 1, null, -14m, 10, null, null, false, 1);
}
catch (ArgumentException ae)
{
Response.Write("There was a problem: " + ae.Message);
}
Jak uvidíme v budoucích kurzech, zpracování výjimek, které pocházejí z vrstvy BLL při použití datového webového ovladače pro vkládání, aktualizaci nebo mazání dat, je možné zpracovávat přímo v obslužné rutině události, takže není nutné kód obalovat do bloků try...catch.
Shrnutí
Dobře navržená aplikace je rozdělena do různých vrstev, z nichž každá zapouzdřuje určitou roli. V prvním kurzu této série článků jsme vytvořili vrstvu přístupu k datům pomocí typed DataSets; v tomto kurzu jsme vytvořili vrstvu obchodní logiky jako řadu tříd ve složce naší aplikace App_Code , která volá do naší DAL. BLL implementuje logiku na úrovni pole a obchodní úrovně pro naši aplikaci. Kromě vytvoření samostatného BLL, jak jsme to udělali v tomto kurzu, je další možností rozšířit metody TableAdapter pomocí částečné třídy. Použití této techniky nám však neumožňuje přepsat stávající metody, ani oddělit naše DAL a BLL tak čistě jako přístup, který jsme popsali v tomto článku.
Po dokončení DAL a BLL jsme připraveni začít na naší prezentační vrstvě. V dalším tutoriálu si uděláme krátkou odbočku od témat přístupu k datům a definujeme konzistentní rozložení stránky pro použití v rámci tutoriálů.
Šťastné programování!
O autorovi
Scott Mitchell, autor sedmi knih ASP/ASP.NET a zakladatel 4GuysFromRolla.com, pracuje s webovými technologiemi Microsoftu od roku 1998. Scott pracuje jako nezávislý konzultant, trenér a spisovatel. Jeho nejnovější kniha je Sams Naučte se ASP.NET 2.0 během 24 hodin. Může být dosažitelný na mitchell@4GuysFromRolla.comadrese .
Zvláštní díky
Tato série kurzů byla zkontrolována mnoha užitečnými recenzenty. Vedoucí hodnotící tohoto kurzu byli Liz Shulok, Dennis Patterson, Carlos Santos a Hilton Giesenow. Chcete si projít nadcházející články MSDN? Pokud ano, napište mi zprávu na mitchell@4GuysFromRolla.com.