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.
Místo vytváření samostatných tříd pro implementaci obchodní logiky bychom mohli tuto logiku umístit přímo do typové datové sady s částečnými třídami. Příklad vytvoření a rozšíření typové datové sady najdete v prvním kurzu.
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
, 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 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;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íTrue
, pokud byl právě jeden řádek aktualizován,False
jinak -
DeleteProduct(productID)
odstraní zadaný produkt z databáze.
ProductsBLL.vb
Imports NorthwindTableAdapters
<System.ComponentModel.DataObject()> _
Public Class ProductsBLL
Private _productsAdapter As ProductsTableAdapter = Nothing
Protected ReadOnly Property Adapter() As ProductsTableAdapter
Get
If _productsAdapter Is Nothing Then
_productsAdapter = New ProductsTableAdapter()
End If
Return _productsAdapter
End Get
End Property
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Select, True)> _
Public Function GetProducts() As Northwind.ProductsDataTable
Return Adapter.GetProducts()
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Select, False)> _
Public Function GetProductByProductID(ByVal productID As Integer) _
As Northwind.ProductsDataTable
Return Adapter.GetProductByProductID(productID)
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Select, False)> _
Public Function GetProductsByCategoryID(ByVal categoryID As Integer) _
As Northwind.ProductsDataTable
Return Adapter.GetProductsByCategoryID(categoryID)
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Select, False)> _
Public Function GetProductsBySupplierID(ByVal supplierID As Integer) _
As Northwind.ProductsDataTable
Return Adapter.GetProductsBySupplierID(supplierID)
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Insert, True)> _
Public Function AddProduct( _
productName As String, supplierID As Nullable(Of Integer), _
categoryID As Nullable(Of Integer), quantityPerUnit As String, _
unitPrice As Nullable(Of Decimal), unitsInStock As Nullable(Of Short), _
unitsOnOrder As Nullable(Of Short), reorderLevel As Nullable(Of Short), _
discontinued As Boolean) _
As Boolean
Dim products As New Northwind.ProductsDataTable()
Dim product As Northwind.ProductsRow = products.NewProductsRow()
product.ProductName = productName
If Not supplierID.HasValue Then
product.SetSupplierIDNull()
Else
product.SupplierID = supplierID.Value
End If
If Not categoryID.HasValue Then
product.SetCategoryIDNull()
Else
product.CategoryID = categoryID.Value
End If
If quantityPerUnit Is Nothing Then
product.SetQuantityPerUnitNull()
Else
product.QuantityPerUnit = quantityPerUnit
End If
If Not unitPrice.HasValue Then
product.SetUnitPriceNull()
Else
product.UnitPrice = unitPrice.Value
End If
If Not unitsInStock.HasValue Then
product.SetUnitsInStockNull()
Else
product.UnitsInStock = unitsInStock.Value
End If
If Not unitsOnOrder.HasValue Then
product.SetUnitsOnOrderNull()
Else
product.UnitsOnOrder = unitsOnOrder.Value
End If
If Not reorderLevel.HasValue Then
product.SetReorderLevelNull()
Else
product.ReorderLevel = reorderLevel.Value
End If
product.Discontinued = discontinued
products.AddProductsRow(product)
Dim rowsAffected As Integer = Adapter.Update(products)
Return rowsAffected = 1
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Update, True)> _
Public Function UpdateProduct(_
productName As String, supplierID As Nullable(Of Integer), _
categoryID As Nullable(Of Integer), quantityPerUnit As String, _
unitPrice As Nullable(Of Decimal), unitsInStock As Nullable(Of Short), _
unitsOnOrder As Nullable(Of Short), reorderLevel As Nullable(Of Short), _
discontinued As Boolean, productID As Integer) _
As Boolean
Dim products As Northwind.ProductsDataTable = _
Adapter.GetProductByProductID(productID)
If products.Count = 0 Then
Return False
End If
Dim product as Northwind.ProductsRow = products(0)
product.ProductName = productName
If Not supplierID.HasValue Then
product.SetSupplierIDNull()
Else
product.SupplierID = supplierID.Value
End If
If Not categoryID.HasValue Then
product.SetCategoryIDNull()
Else
product.CategoryID = categoryID.Value
End If
If quantityPerUnit Is Nothing Then
product.SetQuantityPerUnitNull()
Else
product.QuantityPerUnit = quantityPerUnit
End If
If Not unitPrice.HasValue Then
product.SetUnitPriceNull()
Else
product.UnitPrice = unitPrice.Value
End If
If Not unitsInStock.HasValue Then
product.SetUnitsInStockNull()
Else
product.UnitsInStock = unitsInStock.Value
End If
If Not unitsOnOrder.HasValue Then
product.SetUnitsOnOrderNull()
Else
product.UnitsOnOrder = unitsOnOrder.Value
End If
If Not reorderLevel.HasValue Then
product.SetReorderLevelNull()
Else
product.ReorderLevel = reorderLevel.Value
End If
product.Discontinued = discontinued
Dim rowsAffected As Integer = Adapter.Update(product)
Return rowsAffected = 1
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Delete, True)> _
Public Function DeleteProduct(ByVal productID As Integer) As Boolean
Dim rowsAffected As Integer = Adapter.Delete(productID)
Return rowsAffected = 1
End Function
End Class
Metody, které jednoduše vracejí data GetProducts
, GetProductByProductID
GetProductsByCategoryID
a 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 Nothing
. Další informace najdete v blogovém příspěvku Paula Vicka nazvaném Pravda o typech Nullable a VB a v technické dokumentaci ke struktuře Nullable.
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 Function UpdateSupplierAddress(ByVal supplierID As Integer, _
ByVal address As String, ByVal city As String, ByVal country As String) _
As Boolean
Dim suppliers As Northwind.SuppliersDataTable = _
Adapter.GetSupplierBySupplierID(supplierID)
If suppliers.Count = 0 Then
Return False
Else
Dim supplier As Northwind.SuppliersRow = suppliers(0)
If address Is Nothing Then
supplier.SetAddressNull()
Else
supplier.Address = address
End If
If city Is Nothing Then
supplier.SetCityNull()
Else
supplier.City = city
End If
If country Is Nothing Then
supplier.SetCountryNull()
Else
supplier.Country = country
End If
Dim rowsAffected As Integer = Adapter.Update(supplier)
Return rowsAffected = 1
End If
End Function
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:
Dim productsAdapter As 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
:
Dim productLogic As 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
ProductName
musí mít délku 40 znaků nebo méně. - Pole
QuantityPerUnit
musí mít délku 20 znaků nebo méně. - Pole
ProductID
aProductName
poleDiscontinued
jsou povinná, ale všechna ostatní pole jsou volitelná. - Pole
UnitPrice
,UnitsInStock
,UnitsOnOrder
aReorderLevel
musí 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
, UnitsOnOrder
nebo 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 ProductsDataRow
QuantityPerUnit
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.vb
.
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.vb
Imports System.data
Partial Public Class Northwind
Partial Public Class ProductsDataTable
Public Overrides Sub BeginInit()
AddHandler Me.ColumnChanging, AddressOf ValidateColumn
End Sub
Sub ValidateColumn(sender As Object, e As DataColumnChangeEventArgs)
If e.Column.Equals(Me.UnitPriceColumn) Then
If Not Convert.IsDBNull(e.ProposedValue) AndAlso _
CType(e.ProposedValue, Decimal) < 0 Then
Throw New ArgumentException( _
"UnitPrice cannot be less than zero", "UnitPrice")
End If
ElseIf e.Column.Equals(Me.UnitsInStockColumn) OrElse _
e.Column.Equals(Me.UnitsOnOrderColumn) OrElse _
e.Column.Equals(Me.ReorderLevelColumn) Then
If Not Convert.IsDBNull(e.ProposedValue) AndAlso _
CType(e.ProposedValue, Short) < 0 Then
Throw New ArgumentException(String.Format( _
"{0} cannot be less than zero", e.Column.ColumnName), _
e.Column.ColumnName)
End If
End If
End Sub
End Class
End Class
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
UnitPrice
aktualizovat. - 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
.
<System.ComponentModel.DataObjectMethodAttribute_
(System.ComponentModel.DataObjectMethodType.Update, True)> _
Public Function UpdateProduct( _
productName As String, supplierID As Nullable(Of Integer), _
categoryID As Nullable(Of Integer), quantityPerUnit As String, _
unitPrice As Nullable(Of Decimal), unitsInStock As Nullable(Of Short), _
unitsOnOrder As Nullable(Of Short), reorderLevel As Nullable(Of Short), _
discontinued As Boolean, productID As Integer) _
As Boolean
Dim products As Northwind.ProductsDataTable = _
Adapter.GetProductByProductID(productID)
If products.Count = 0 Then
Return False
End If
Dim product As Northwind.ProductsRow = products(0)
If discontinued Then
Dim productsBySupplier As Northwind.ProductsDataTable = _
Adapter.GetProductsBySupplierID(product.SupplierID)
If productsBySupplier.Count = 1 Then
Throw New ApplicationException( _
"You cannot mark a product as discontinued if it is " & _
"the only product purchased from a supplier")
End If
End If
product.ProductName = productName
If Not supplierID.HasValue Then
product.SetSupplierIDNull()
Else
product.SupplierID = supplierID.Value
End If
If Not categoryID.HasValue Then
product.SetCategoryIDNull()
Else
product.CategoryID = categoryID.Value
End If
If quantityPerUnit Is Nothing Then
product.SetQuantityPerUnitNull()
Else
product.QuantityPerUnit = quantityPerUnit
End If
If Not unitPrice.HasValue Then
product.SetUnitPriceNull()
Else
product.UnitPrice = unitPrice.Value
End If
If Not unitsInStock.HasValue Then
product.SetUnitsInStockNull()
Else
product.UnitsInStock = unitsInStock.Value
End If
If Not unitsOnOrder.HasValue Then
product.SetUnitsOnOrderNull()
Else
product.UnitsOnOrder = unitsOnOrder.Value
End If
If Not reorderLevel.HasValue Then
product.SetReorderLevelNull()
Else
product.ReorderLevel = reorderLevel.Value
End If
product.Discontinued = discontinued
Dim rowsAffected As Integer = Adapter.Update(product)
Return rowsAffected = 1
End Function
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 HttpApplication
Error
). K zpracování výjimky při práci s BLL programově můžeme použít Try...Catch blok, jak ukazuje následující příklad:
Dim productLogic As New ProductsBLL()
Try
productLogic.UpdateProduct("Scotts Tea", 1, 1, Nothing, _
-14, 10, Nothing, Nothing, False, 1)
Catch ae As ArgumentException
Response.Write("There was a problem: " & ae.Message)
End Try
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č se ASP.NET 2.0 za 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.