Vytvoření vrstvy obchodní logiky (VB)
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. I když dal čistě odděluje podrobnosti o přístupu k datům od prezentační vrstvy, nevynucuje žádná obchodní pravidla, která by se můžou použít. U naší aplikace můžeme například zakázat úpravu CategoryID
Products
polí nebo SupplierID
tabulky, 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 ho najal. Dalším běžným scénářem je autorizace, která může odstranit produkty nebo změnit UnitPrice
hodnotu pouze uživatelům 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ěl být implementován 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 vztahy architektury mezi prezentační vrstvou, BLL a DAL.
Obrázek 1: Funkce 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 tuto logiku mohli případně 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 se bude skládat ze čtyř tříd, jedné pro každou 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.
Pokud chcete přesněji oddělit třídy související 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 sadu Typed DataSet vytvořenou v prvním kurzu do podsložky DAL
.
Dále v BLL
podsložce vytvořte čtyři soubory tříd BLL. Chcete-li to provést, klikněte pravým tlačítkem na BLL
podsložku, zvolte Přidat novou položku a zvolte šablonu Třída. Pojmenujte čtyři třídy ProductsBLL
, SuppliersBLL
CategoriesBLL
, a EmployeesBLL
.
Obrázek 2: Přidání čtyř nových tříd do App_Code
složky
Dále do každé třídy přidáme metody, které jednoduše zabalí metody definované pro Objekty TableAdapter z prvního kurzu. Prozatím budou 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 o třídě Designer.
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 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
jinakDeleteProduct(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. I když 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, ke které uživatel patří), tyto metody jednoduše ponecháme tak, jak jsou. Pro tyto metody pak BLL slouží pouze jako proxy server, jehož prostřednictvím 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řidávají nový produkt nebo aktualizují existující produkt. Vzhledem k tomu, SupplierID
že mnoho Product
sloupců tabulky může přijímat NULL
hodnoty (CategoryID
, a UnitPrice
, abychom jmenovali několik), tyto vstupní parametry pro AddProduct
aUpdateProduct
, 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 Nothing
hodnotový typ místo toho . Další informace najdete v blogové položce Paula VickaPravda o typech s možnou hodnotou null a VB a v technické dokumentaci pro strukturu 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 zavolá předání pro ProductID
neexistující produkt, DELETE
příkaz vydaný do databáze 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 přijetí ProductsRow
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
nástroji ). Tento nedostatek se vymění za hlavu, když se obrátíme na vkládání a aktualizaci produktů pomocí 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 nedostatku výchozího konstruktoru 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 pomocí Strongly-Typed datových sad a Problém s objekty ObjectDataSource a Strongly-Typed DataSet.
Dále v nástroji i AddProduct
UpdateProduct
vytvoří ProductsRow
kód 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 ověřovacím kontrolám 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. Místo toho, abychom označit, že konkrétní DataColumn v DataRow by měl odpovídat hodnotě NULL
databáze, musíme použít metodu SetColumnNameNull()
.
V UpdateProduct
nástroji nejprve načteme produkt a aktualizujeme ho 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 ukáže jako užitečný v budoucích kurzech, které zkoumají optimistickou souběžnost. 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ž prozkoumáme SuppliersBLL
třídu, 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, ObjectDataSource ASP.NET 2.0 usnadňuje deklarativně přístup k datům z třídy. Chcete-li pomoct filtrovat seznam možných tříd, které se mají svá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í usnadňuje práci s v průvodci ObjectDataSource.
Přidání dalších tříd
ProductsBLL
Po dokončení třídy musíme přidat třídy pro práci s kategoriemi, dodavateli a zaměstnanci. Využijte koncepty z výše uvedeného příkladu k vytvoření následujících tříd a metod:
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 čte v objektu SupplierDataRow
pro zadaný supplierID
(pomocí GetSupplierBySupplierID
), nastaví jeho vlastnosti související s adresou a pak zavolá do SupplierDataTable
metody 's Update
. Metoda je UpdateSupplierAddress
následující:
<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
Kompletní implementaci tříd BLL najdete ve stažení tohoto článku.
Krok 2: Přístup k zadaným datovým sadám 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
byl použit k vytvoření vazby seznamu produktů na 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, stačí změnit pouze první řádek kódu a jednoduše nahradit ProductsTableAdapter
objekt objektem ProductBLL
:
Dim productLogic As New ProductsBLL()
GridView1.DataSource = productLogic.GetProducts()
GridView1.DataBind()
K třídám BLL lze také přistupovat deklarativně (stejně jako Typed DataSet) pomocí ObjectDataSource. ObjectDataSource si podrobněji probereme 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 pole 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.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 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.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 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
.
<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 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 try... Blok catch , 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é 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.