Vytvoření vrstvy obchodní logiky (C#)
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 DAL (Data Access Layer) vytvořená v prvním kurzu čistě odděluje logiku přístupu k datům od logiky prezentace. I když ale DAL čistě odděluje podrobnosti o přístupu k datům od prezentační vrstvy, nevynucuje žádná obchodní pravidla, která by se dala použít. Pro naši aplikaci můžeme například zakázat úpravu CategoryID
Products
polí nebo SupplierID
v tabulce, když Discontinued
je pole nastaveno na hodnotu 1, nebo můžeme chtít vynutit pravidla seniority, která zakazují situace, kdy je zaměstnanec spravován někým, kdo byl přijat po nich. Dalším běžným scénářem je autorizace, která může odstranit produkty nebo změnit UnitPrice
hodnotu pouze uživatelů v určité roli.
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 BLL měla být implementována 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šovali strukturu projektu. Obrázek 1 znázorňuje architektonické vztahy 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 se bude skládat ze čtyř tříd, jednu pro každý TableAdapter v DAL; každá z těchto tříd BLL bude mít metody pro načtení, vložení, aktualizaci a odstranění z příslušného objektu TableAdapter v DAL, přičemž se použijí příslušná obchodní pravidla.
Pro přehlednější oddělení tříd souvisejících s DAL a BLL vytvoříme ve App_Code
složce DAL
dvě podsložky a BLL
. Jednoduše klikněte pravým tlačítkem na App_Code
složku v Průzkumník řešení a zvolte Nová složka. Po vytvoření těchto dvou složek přesuňte Typed DataSet vytvořenou v prvním kurzu do podsložky DAL
.
Dále v BLL
podsložce vytvořte čtyři soubory třídy BLL. To provedete tak, že kliknete pravým tlačítkem na BLL
podsložku, zvolíte Přidat novou položku a zvolíte šablonu Třída. Pojmenujte čtyři třídy ProductsBLL
, CategoriesBLL
, SuppliersBLL
a EmployeesBLL
.
Obrázek 2: Přidání čtyř nových tříd do App_Code
složky
V dalším kroku přidáme do každé třídy metody, které jednoduše zabalí metody definované pro objekty TableAdapter z prvního kurzu. Prozatím bude tyto metody pouze volat přímo do DAL; později se vrátíme a přidáme veškerou 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í třídy Designer. Další informace o této nové funkci v sadě Visual Studio najdete na blogu Designer třídy.
ProductsBLL
Pro třídu musíme 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 dodavateleAddProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued)
vloží nový produkt do databáze pomocí předaných hodnot;ProductID
vrá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í hodnotutrue
, pokud byl právě jeden řádek aktualizován,false
jinakDeleteProduct(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 vrací data GetProducts
, GetProductByProductID
, GetProductsByCategoryID
a GetProductBySuppliersID
jsou poměrně jednoduché, protože jednoduše volají do DAL. I když v některých scénářích můžou 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 beze stavu. Pro tyto metody pak BLL slouží pouze jako proxy, přes který prezentační vrstva přistupuje k podkladovým datům z vrstvy přístupu k datům.
Metody AddProduct
a UpdateProduct
přebírají jako parametry hodnoty pro různá pole produktů a přidají nový produkt nebo aktualizují existující. Vzhledem k tomu, že mnoho Product
sloupců tabulky může přijímat NULL
hodnoty (CategoryID
, SupplierID
a , abychom UnitPrice
jmenovali jen některé), tyto vstupní parametry pro AddProduct
a UpdateProduct
, které se mapují na tyto sloupce, používají typy s možnou hodnotou null. Typy s možnou hodnotou null jsou v rozhraní .NET 2.0 nové a poskytují techniku pro určení, jestli by měl být null
hodnotový typ místo toho . V jazyce C# můžete označit typ hodnoty 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 zavolá předání pro ProductID
neexistující produkt, DELETE
příkaz vystavený databázi nebude mít žádný vliv, a proto DeleteProduct
metoda vrátí false
.DeleteProduct
Všimněte si, že při přidávání nového produktu nebo aktualizaci existujícího produktu bereme hodnoty polí nového nebo upraveného produktu jako seznam skalárů, nikoli jako ProductsRow
přijetí instance. Tento přístup byl zvolen, protože ProductsRow
třída je odvozena od třídy ADO.NETDataRow
, 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 (což děláme v AddProduct
). Tento nedostatek se vymění za hlavu, když přejdeme k vkládání a aktualizaci produktů pomocí objektu ObjectDataSource. 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, ale selže kvůli chybějící výchozí konstruktor bez parametrů. Další informace o tomto problému najdete v následujících dvou příspěvcích ASP.NET fór: Aktualizace ObjectDataSources s Strongly-Typed datových sad a Problém s ObjectDataSource a Strongly-Typed DataSet.
Dále v systémech a AddProduct
UpdateProduct
vytvoří kód ProductsRow
instanci a naplní ji hodnotami, které jste právě předali. Při přiřazování hodnot do objektů DataColumns dataRow může dojít k různým kontrolám ověření na úrovni pole. Proto ruční vložení předaných hodnot zpět do DataRow pomáhá zajistit platnost dat předávaných do metody BLL. Třídy DataRow se silnými typy vygenerované sadou Visual Studio bohužel nepoužívají typy s možnou hodnotou null. Pokud chcete označit, že konkrétní sloupec DataColumn v objektu DataRow by měl odpovídat hodnotě NULL
databáze, musíme použít metodu SetColumnNameNull()
.
V UpdateProduct
nástroji nejprve načteme produkt, který aktualizujeme pomocí GetProductByProductID(productID)
. I když se to může zdát jako zbytečný výlet do databáze, tento dodatečný výlet se v budoucích kurzech, které se zabývají optimistickou souběžností, se osvědčí. Optimistická souběžnost je technika, která zajišťuje, aby se dva uživatelé, kteří současně pracují na stejných datech, omylem nepřepsali změny druhého. Získání celého záznamu také usnadňuje vytváření metod aktualizace v BLL, které upravují pouze podmnožinu sloupců DataRow. Když třídu prozkoumáme SuppliersBLL
, uvidíme takový příklad.
Nakonec si všimněte, že ProductsBLL
třída má použitý atribut DataObject ( [System.ComponentModel.DataObject]
syntaxe přímo před příkazem class v horní části souboru) a metody mají atributy DataObjectMethodAttribute. Atribut DataObject
označuje třídu jako objekt vhodný pro vazbu na ObjectDataSource ovládací prvek, 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. Aby bylo možné filtrovat seznam možných tříd, které se mají navázat v průvodci ObjectDataSource, jsou ve výchozím nastavení v rozevíracím seznamu průvodce zobrazeny pouze třídy označené jako DataObjects
. Třída ProductsBLL
bude fungovat stejně dobře bez těchto atributů, ale jejich přidání usnadní práci s v průvodci ObjectDataSource.
Přidání dalších tříd
ProductsBLL
Po dokončení třídy stále potřebujeme přidat třídy pro práci s kategoriemi, dodavateli a zaměstnanci. Vytvořte následující třídy a metody pomocí 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)
Jedinou metodou, která stojí za zmínku, SuppliersBLL
je metoda třídy UpdateSupplierAddress
. Tato metoda poskytuje rozhraní pro aktualizaci pouze informací o adrese dodavatele. Interně tato metoda načte objekt pro zadaný supplierID
objekt (pomocí GetSupplierBySupplierID
), nastaví jeho vlastnosti související s adresou a pak zavolá metodu SupplierDataTable
Update
.SupplierDataRow
Metoda UpdateSupplierAddress
je následující:
[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;
}
}
Kompletní implementaci tříd BLL najdete v tomto článku ke stažení.
Krok 2: Přístup k typed dataSets prostřednictvím tříd BLL
V prvním kurzu jsme viděli příklady přímé práce s typed dataSet programově, ale s přidáním našich tříd BLL by prezentační vrstva měla místo toho fungovat s BLL. V příkladu AllProducts.aspx
z prvního kurzu ProductsTableAdapter
byl použit k vytvoření vazby seznamu produktů na GridView, jak je znázorněno v následujícím kódu:
ProductsTableAdapter productsAdapter = new ProductsTableAdapter();
GridView1.DataSource = productsAdapter.GetProducts();
GridView1.DataBind();
Pokud chcete použít nové třídy BLL, stačí, když první řádek kódu jednoduše nahradí ProductsTableAdapter
objekt objektem ProductBLL
:
ProductsBLL productLogic = new ProductsBLL();
GridView1.DataSource = productLogic.GetProducts();
GridView1.DataBind();
Třídy BLL lze také přistupovat deklarativně (stejně jako Typed DataSet) pomocí ObjectDataSource. Objektu ObjectDataSource se budeme podrobněji věnovat v následujících kurzech.
Obrázek 3: Seznam produktů se zobrazí v zobrazení GridView (kliknutím zobrazíte obrázek v plné velikosti)
Krok 3: Přidání ověřování Field-Level do tříd DataRow
Ověření 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 polí pro produkty patří:
- Pole
ProductName
musí mít délku nejméně 40 znaků. - Pole
QuantityPerUnit
musí mít délku nejméně 20 znaků. - Pole
ProductID
,ProductName
aDiscontinued
jsou povinná, ale všechna ostatní pole jsou volitelná. - Pole
UnitPrice
,UnitsInStock
,UnitsOnOrder
aReorderLevel
musí být větší než nebo rovna nule.
Tato pravidla je možné a měla by být vyjádřena na úrovni databáze. Omezení ProductName
počtu znaků v polích a QuantityPerUnit
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 volitelná, pokud sloupec tabulky databáze umožňuje NULL
s. Existují čtyři omezení kontroly , která zajišťují, že do UnitPrice
sloupců , UnitsInStock
, UnitsOnOrder
, nebo ReorderLevel
se můžou dostat pouze hodnoty větší nebo rovné nule.
Kromě vynucování těchto pravidel v databázi by se měla vynucovat také na úrovni datové sady. Ve skutečnosti se délka pole a to, jestli je hodnota povinná nebo volitelná, už zaznamenávají pro každou sadu DataTable DataColumns. Pokud chcete zobrazit existující ověření na úrovni pole automaticky, přejděte na Designer Datová sada, vyberte pole z některé z datových tabulek a pak přejděte na okno Vlastnosti. Jak ukazuje obrázek 4, QuantityPerUnit
sloupec DataColumn v souboru ProductsDataTable
má maximální délku 20 znaků a povoluje NULL
hodnoty. Pokud se pokusíme nastavit ProductsDataRow
QuantityPerUnit
vlastnost na řetězcovou hodnotu delší než 20 znaků, ArgumentException
vyvolá se to.
Obrázek 4: DataColumn poskytuje základní Field-Level ověření (kliknutím zobrazíte obrázek v plné velikosti)
Bohužel prostřednictvím okno Vlastnosti nemůžeme určit kontroly hranic, například UnitPrice
hodnota musí být větší než nebo rovna nule. 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 bylo zmíněno v předchozím kurzu, DataSet, DataTables a DataRow objekty vytvořené Typed DataSet lze rozšířit pomocí částečných tříd. Pomocí této techniky můžeme vytvořit obslužnou rutinu ColumnChanging
ProductsDataTable
události pro třídu . 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 ColumnChanging
události pro událost, která zajistí, aby UnitPrice
hodnoty sloupců , UnitsInStock
UnitsOnOrder
, a ReorderLevel
(pokud neNULL
) byly větší než nebo se rovna nule. Pokud je některý z takových sloupců mimo rozsah, vyvolá se 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 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 na úrovni jednoho sloupce nedají vyjádřit, například:
- Pokud je produkt ukončen, nelze ho
UnitPrice
aktualizovat. - Země bydliště zaměstnance musí být stejná jako země bydliště nadřízenýho.
- Produkt nelze ukončit, pokud se jedná o jediný produkt poskytnutý 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 nelze označit jako ukončený, pokud se jedná o jediný produkt od daného dodavatele. To znamená, že pokud byl produkt X jediným produktem, který jsme zakoupili od dodavatele Y, nemohli bychom označit X jako ukončený; Pokud nám však dodavatel Y dodal tři výrobky , A, B a C, pak bychom mohli označit všechny tyto produkty jako ukončené. Zvláštní obchodní pravidlo, ale obchodní pravidla a zdravý rozum nejsou vždy sladěné!
Pokud chceme toto obchodní pravidlo vynutit v UpdateProducts
metodě, začneme tím, že zkontrolujeme, jestli Discontinued
je nastavené na true
hodnotu , a pokud ano, zavoláme GetProductsBySupplierID
, abychom zjistili, kolik produktů jsme koupili 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 se můžeme rozhodnout, jestli se pokusíme zpracovat případné výjimky, které by mohly být vyvolány, nebo je nechat vybouchnout až do ASP.NET (což vyvolá HttpApplication
událost ).Error
Ke zpracování výjimky při práci s BLL prostřednictvím kódu programu můžeme použít ... blok catch , 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é se vygenerují z BLL při použití webového datového ovládacího prvku pro vkládání, aktualizaci nebo odstraňování dat, může být zpracováno přímo v obslužné rutině události, na rozdíl od zalamování kódu do try...catch
bloků.
Souhrn
Dobře navržená aplikace je sestavená do odlišný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é volají dolů do 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šíření metod TableAdapter pomocí částečných tříd. Použití této techniky nám však neumožňuje přepsat existující metody ani neodděluje dal a BLL tak čistě jako přístup, který jsme provedli v tomto článku.
Po dokončení DAL a BLL jsme připraveni začít s naší prezentační vrstvou. V dalším kurzu se krátce podíváme na témata o přístupu k datům a nadefinujeme konzistentní rozložení stránky, které se bude používat v rámci kurzů.
Všechno nejlepší na 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 zastihnout na mitchell@4GuysFromRolla.comadrese . nebo prostřednictvím jeho blogu, který najdete na adrese http://ScottOnWriting.NET.
Zvláštní poděkování
Tato série kurzů byla zkontrolována mnoha užitečnými recenzenty. Hlavními recenzenty pro tento kurz byli Liz Shulok, Dennis Patterson, Carlos Santos a Hilton Giesenow. Chtěli byste si projít své nadcházející články na webu MSDN? Pokud ano, dejte mi řádek na mitchell@4GuysFromRolla.com.