Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
által Scott Mitchell
Ebben az oktatóanyagban bemutatjuk, hogyan központosíthatja üzleti szabályait egy üzleti logikai rétegre (BLL), amely közvetítőként szolgál a bemutató réteg és a DAL közötti adatcseréhez.
Bevezetés
Az első oktatóanyagban létrehozott adatelérési réteg (DAL) tisztán elkülöníti az adatelérési logikát a bemutató logikától. Bár a DAL egyértelműen elkülöníti az adathozzáférési adatokat a bemutató rétegtől, nem kényszeríti ki az esetlegesen alkalmazandó üzleti szabályokat. Előfordulhat például, hogy az alkalmazás esetében le szeretnénk tiltani a CategoryID tábla vagy SupplierID mezők Products módosítását, ha a Discontinued mező értéke 1, vagy szeretnénk kikényszeríteni a rangidőre vonatkozó szabályokat, tiltva azokat a helyzeteket, amikor egy alkalmazottat olyan személy kezel, akit utána béreltek fel. Egy másik gyakori forgatókönyv az engedélyezés, talán csak az adott szerepkörben lévő felhasználók törölhetik a termékeket, vagy módosíthatják az UnitPrice értéket.
Ez az oktatóanyag bemutatja, hogyan központosíthatja ezeket az üzleti szabályokat egy üzleti logikai rétegre (BLL), amely közvetítőként szolgál a bemutató réteg és a DAL közötti adatcseréhez. Egy valós alkalmazásban a BLL-t külön Osztálytár-projektként kell megvalósítani; Ezekhez az oktatóanyagokhoz azonban a BLL-t osztálysorozatként fogjuk implementálni a mappánkban, hogy egyszerűsítsük App_Code a projektstruktúrát. Az 1. ábra a bemutató réteg, a BLL és a DAL architektúrakapcsolatait szemlélteti.
1. ábra: A BLL elkülöníti a bemutató réteget az adatelérési rétegtől, és üzleti szabályokat ír elő
Ahelyett, hogy külön osztályokat hoznánk létre az üzleti logika megvalósításához, ezt a logikát közvetlenül a részleges osztályokkal rendelkező gépelt adatkészletbe helyezhetjük. A beírt adathalmaz létrehozására és kiterjesztésére vonatkozó példáért tekintse meg az első oktatóanyagot.
1. lépés: A BLL-osztályok létrehozása
A BLL négy osztályból áll, egyet a DAL minden TableAdapteréhez; A BLL-osztályok mindegyike rendelkezik metódusokkal a megfelelő üzleti szabályok alkalmazásával a megfelelő TableAdapter táblaadapterből való lekérésére, beszúrására, frissítésére és törlésére.
A DAL- és BLL-hez kapcsolódó osztályok pontosabb elkülönítéséhez hozzunk létre két almappát a App_Code mappában: DAL és BLL. Egyszerűen kattintson a jobb gombbal a App_Code megoldáskezelő mappájára, és válassza az Új mappa lehetőséget. A két mappa létrehozása után helyezze át az első oktatóanyagban létrehozott Typed DataSetet az DAL almappába.
Ezután hozza létre a négy BLL-osztályfájlt az BLL almappában. Ehhez kattintson a jobb gombbal az almappára, válassza az BLL Új elem hozzáadása lehetőséget, és válassza az Osztálysablont. Nevezze el a négy osztályt ProductsBLL, CategoriesBLLSuppliersBLLés EmployeesBLL.
2. ábra: Négy új osztály hozzáadása a App_Code mappához
Ezután adjunk hozzá metódusokat az egyes osztályokhoz, hogy egyszerűen becsomagoljuk a TableAdaptershez definiált metódusokat az első oktatóanyagból. Egyelőre ezek a módszerek közvetlenül a DAL-t fogják hívni, később pedig visszatérünk, hogy hozzáadjunk minden szükséges üzleti logikát.
Megjegyzés:
Ha Visual Studio Standard Edition vagy újabb kiadást használ (vagyis nem a Visual Web Developert használja), igény szerint vizuálisan is megtervezheti az osztályokat az Osztálytervezővel. A Visual Studio új funkciójával kapcsolatos további információkért tekintse meg az Osztálytervező blogot .
Az ProductsBLL osztályhoz összesen hét metódust kell hozzáadnunk:
-
GetProducts()az összes terméket visszaadja -
GetProductByProductID(productID)a megadott termékazonosítóval rendelkező terméket adja vissza -
GetProductsByCategoryID(categoryID)a megadott kategóriából származó összes terméket visszaadja -
GetProductsBySupplier(supplierID)a megadott szállítótól származó összes terméket visszaadja -
AddProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued)új terméket szúr be az adatbázisba a megadott értékekkel; azProductIDújonnan beszúrt rekord értékét adja vissza -
UpdateProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued, productID)frissít egy meglévő terméket az adatbázisban az átadott értékekkel; visszaadjaTrue, ha pontosan egy sor frissült,Falseellenkező esetben -
DeleteProduct(productID)törli a megadott terméket az adatbázisból
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
Azok a metódusok, amelyek egyszerűen visszaadják az adatokatGetProducts, GetProductByProductIDGetProductsByCategoryIDés GetProductBySuppliersID meglehetősen egyszerűek, mivel egyszerűen lehívják a DAL-t. Bizonyos esetekben előfordulhatnak olyan üzleti szabályok, amelyeket ezen a szinten kell implementálnunk (például az aktuálisan bejelentkezett felhasználó vagy a felhasználó szerepköre alapján történő engedélyezési szabályokat), ezeket a metódusokat egyszerűen as-ishagyjuk. Ezeknél a módszereknél a BLL csupán proxyként szolgál, amelyen keresztül a bemutató réteg hozzáfér a mögöttes adatokhoz az adatelérési rétegből.
Az AddProduct és UpdateProduct metódusok mindketten paraméterként használják a különböző termékmezők értékeit, és sorrendben egy új terméket adnak hozzá, illetve frissítenek egy meglévőt. Mivel a Product tábla számos oszlopa elfogadhat NULL értékeket (CategoryID, SupplierID, és UnitPrice, hogy csak példát említsünk), azok a bemeneti paraméterek a AddProduct és UpdateProduct esetében, amelyek az ilyen oszlopokra vannak leképezve, null értékű típusokat használnak. A null értékű típusok újdonságnak számítanak a .NET 2.0-ban, és olyan technikát biztosítanak, amely azt jelzi, hogy egy értéktípus null értéket vehet-e fel. További információért tekintse meg Paul Vick blogbejegyzését: Az igazság a nullázható típusokról és a VB-ről , valamint a nullázható struktúra műszaki dokumentációját.
Mindhárom metódus logikai értéket ad vissza, amely jelzi, hogy egy sor beszúrása, frissítése vagy törlése történt-e, mivel a művelet nem eredményez érintett sort. Ha például az oldal fejlesztője nem létező termékhez ad át DeleteProduct-t, az adatbázisnak kiadott ProductID utasításnak nem lesz semmilyen hatása, ezért a DELETE metódus visszatér DeleteProduct-val.
Vegye figyelembe, hogy új termék hozzáadásakor vagy meglévő frissítésekor az új vagy módosított termék mezőértékeit skaláris listaként vesszük fel, nem pedig egy ProductsRow példány elfogadásakor. Ez a megközelítés azért lett kiválasztva, mert az osztály a ProductsRow ADO.NET DataRow osztályból származik, amely nem rendelkezik alapértelmezett paraméter nélküli konstruktorsal. Egy új ProductsRow példány létrehozásához először létre kell hoznunk egy példányt ProductsDataTable , majd meg kell hívnunk annak metódusát NewProductRow() (ebben a példában AddProduct). Ez a hiányosság felbukkan, amikor a termékek beszúrásához és frissítéséhez az ObjectDataSource-t használjuk. Röviden: az ObjectDataSource megpróbálja létrehozni a bemeneti paraméterek egy példányát. Ha a BLL metódus egy ProductsRow példányt vár, az ObjectDataSource megpróbál létrehozni egyet, de mivel hiányzik az alapértelmezett, paraméter nélküli konstruktor, ez nem sikerül. A problémával kapcsolatos további információkért tekintse meg a következő két ASP.NET fórum bejegyzést: Az ObjectDataSources frissítése Strongly-Typed adatkészletekkel, valamint az ObjectDataSource és Strongly-Typed adatkészlettel kapcsolatos probléma.
Ezután mind a AddProduct-ban, mind a UpdateProduct-ben a kód létrehoz egy ProductsRow példányt, és feltölti az imént átadott értékekkel. Amikor a DataRow DataColumnjaihoz rendelünk értékeket, különböző mezőszintű érvényesítési ellenőrzések történhetnek. Ezért az átadott értékeknek a DataRow-ba való manuális visszavétele biztosítja a BLL-metódusnak átadott adatok érvényességét. A Visual Studio által létrehozott, erősen gépelt DataRow-osztályok sajnos nem használnak null értékű típusokat. Ehelyett annak jelzéséhez, hogy a DataRow-ban egy adott DataColumnnak meg kell felelnie egy NULL adatbázis-értéknek, a SetColumnNameNull() módszert kell használnunk.
Először betöltjük a terméket a frissítéshez a következővel UpdateProductGetProductByProductID(productID). Bár ez szükségtelen adatbázislekérdezésnek tűnhet, ez az extra lekérdezés hasznosnak bizonyulni fog az optimista egyidejűséget feltáró jövőbeli oktatóanyagokban. Az optimista egyidejűség egy olyan technika, amellyel biztosítható, hogy az ugyanazon adatokon egyidejűleg dolgozó két felhasználó ne írja felül véletlenül egymás módosításait. A teljes rekord megragadásával egyszerűbben hozhat létre olyan frissítési metódusokat a BLL-ben, amelyek csak a DataRow oszlopainak egy részét módosítják. Amikor felfedezzük az SuppliersBLL osztályt, egy ilyen példát fogunk látni.
Végül vegye figyelembe, hogy az ProductsBLL osztályra a DataObject attribútum van alkalmazva (a [System.ComponentModel.DataObject] szintaxis közvetlenül az osztályutasítás előtt a fájl tetején), és a metódusok DataObjectMethodAttribute attribútumokkal rendelkeznek. Az DataObject attribútum az osztályt objektumként jelöli, amely alkalmas az ObjectDataSource-vezérlőhöz való kötésre, míg a DataObjectMethodAttribute metódus célját jelzi. Ahogy a jövőbeli oktatóanyagokban is látni fogjuk, ASP.NET 2.0 ObjectDataSource-jával egyszerűen deklaratív módon érheti el az adatokat egy osztályból. Ha szeretné szűrni az ObjectDataSource varázslójában kötendő lehetséges osztályok listáját, alapértelmezés szerint csak azok az osztályok vannak megjelölve, amelyek DataObjects a varázsló legördülő listájában láthatók. Az ProductsBLL osztály ugyanúgy működik ezek nélkül az attribútumok nélkül, de a hozzáadásuk megkönnyíti a munkát az ObjectDataSource varázslójában.
Az egyéb osztályok hozzáadása
Miután a ProductsBLL osztály elkészült, még hozzá kell adnunk a kategóriákkal, beszállítókkal és alkalmazottakkal való munkához szükséges osztályokat. Szánjon egy kis időt a következő osztályok és metódusok létrehozására a fenti példában szereplő fogalmak alapján:
CategoriesBLL.cs
GetCategories()GetCategoryByCategoryID(categoryID)
SuppliersBLL.cs
GetSuppliers()GetSupplierBySupplierID(supplierID)GetSuppliersByCountry(country)UpdateSupplierAddress(supplierID, address, city, country)
EmployeesBLL.cs
GetEmployees()GetEmployeeByEmployeeID(employeeID)GetEmployeesByManager(managerID)
Az egyetlen módszer, amelyet érdemes megjegyezni, az osztály SuppliersBLL metódusa UpdateSupplierAddress . Ez a módszer csak a szállító címadatainak frissítéséhez biztosít felületet. Ez a metódus belsőleg beolvassa a SupplierDataRow objektumot a megadott supplierID használatával, beállítja annak címmel kapcsolatos tulajdonságait, majd lehívja a GetSupplierBySupplierIDSupplierDataTable metódusát. A UpdateSupplierAddress módszer a következő:
<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
A BLL-osztályok teljes implementációjához tekintse meg a cikk letöltését.
2. lépés: A beírt adathalmazok elérése a BLL-osztályokon keresztül
Az első oktatóanyagban példákat láthattunk arra, hogy a gépelt adathalmaz programozott módon működik, de a BLL-osztályok hozzáadásával a bemutatószintnek inkább a BLL-vel kell működnie.
AllProducts.aspx Az első oktatóanyagban szereplő példában a ProductsTableAdapter termékek listáját egy GridView-hoz kötötték, ahogyan az a következő kódban is látható:
Dim productsAdapter As New ProductsTableAdapter()
GridView1.DataSource = productsAdapter.GetProducts()
GridView1.DataBind()
Az új BLL-osztályok használatához mindössze annyit kell módosítani, hogy az első kódsor egyszerűen lecseréli az ProductsTableAdapter objektumot egy ProductBLL objektumra:
Dim productLogic As New ProductsBLL()
GridView1.DataSource = productLogic.GetProducts()
GridView1.DataBind()
A BLL-osztályok deklaratív módon is elérhetők (ahogy a Typed DataSet is) az ObjectDataSource használatával. Az ObjectDataSource-ról részletesebben az alábbi oktatóanyagokban lesz szó.
3. ábra: A termékek listája megjelenik egy GridView-ban (kattintson ide a teljes méretű kép megtekintéséhez)
3. lépés: Field-Level-ellenőrzés hozzáadása a DataRow-osztályokhoz
A mezőszintű ellenőrzések azok az üzleti objektumok tulajdonságértékeivel kapcsolatos vizsgálatok, amelyek beszúráskor vagy frissítésekor történnek. A termékek mezőszintű érvényesítési szabályai közé tartoznak a következők:
- A
ProductNamemező legfeljebb 40 karakter hosszúságú lehet - A
QuantityPerUnitmező legfeljebb 20 karakter hosszúságú lehet - A
ProductID,ProductNameésDiscontinueda mezők megadása kötelező, de az összes többi mező nem kötelező - A
UnitPrice,UnitsInStock,UnitsOnOrderésReorderLevelmezőknek nullánál nagyobbnak vagy egyenlőnek kell lenniük
Ezeket a szabályokat az adatbázis szintjén lehet és kell kifejezni. A ProductName és QuantityPerUnit mezők karakterkorlátját a Products táblázat oszlopainak adattípusai rögzítik (nvarchar(40) és nvarchar(20) esetében). Annak kifejezése, hogy a mezők kötelezőek vagy választhatók, attól függ, hogy az adatbázistábla oszlopa engedélyezi-e a NULL s-t. Négy ellenőrzési feltétel létezik, amelyek biztosítják, hogy csak a nullánál nagyobb vagy egyenlő értékek kerülhessenek a UnitPrice, UnitsInStock, UnitsOnOrder, vagy ReorderLevel oszlopokba.
Ezen szabályok az adatbázison történő kényszerítése mellett az adathalmaz szintjén is kikényszeríthetők. Valójában minden egyes DataTable esetében a DataColumnok halmazának már rögzítve van a mező hossza, valamint az, hogy egy érték szükséges vagy opcionális. Ha automatikusan meg szeretné tekinteni a meglévő mezőszintű ellenőrzést, nyissa meg az Adatkészlettervezőt, jelöljön ki egy mezőt az egyik Adattáblából, majd lépjen a Tulajdonságok ablakra. A 4. ábrán látható, hogy a QuantityPerUnit DataColumn ProductsDataTable legfeljebb 20 karakter hosszúságú, és engedélyezi NULL az értékeket. Ha megpróbáljuk a ProductsDataRowQuantityPerUnit tulajdonságot 20 karakternél hosszabb sztringértékre beállítani, akkor ArgumentException keletkezik.
4. ábra: A DataColumn alapszintű Field-Level érvényesítést biztosít (kattintson ide a teljes méretű kép megtekintéséhez)
Sajnos a Tulajdonságok ablakban nem adhatók meg a határellenőrzések, például az UnitPrice értéknek nullánál nagyobbnak vagy egyenlőnek kell lennie. Az ilyen típusú mezőszintű ellenőrzés biztosításához létre kell hoznunk egy eseménykezelőt a DataTable ColumnChanging eseményéhez. Ahogy az előző oktatóanyagban is említettük, a Gépelt adatkészlet által létrehozott DataSet, DataTables és DataRow objektumok részleges osztályok használatával bővíthetők. Ezzel a technikával létrehozhatunk egy eseménykezelőt ColumnChanging az ProductsDataTable osztály számára. Először hozzon létre egy osztályt a App_Code nevű ProductsDataTable.ColumnChanging.vbmappában.
5. ábra: Új osztály hozzáadása a App_Code mappához (kattintson ide a teljes méretű kép megtekintéséhez)
Ezután hozzon létre egy eseménykezelőt az ColumnChanging eseményhez, amely biztosítja, hogy a UnitPrice, UnitsInStock, UnitsOnOrder, és ReorderLevel oszlop értékei (ha nem NULL) nullánál nagyobbak vagy egyenlők legyenek. Ha bármelyik ilyen oszlop kívül esik a tartományon, dobja a ArgumentException-t.
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
4. lépés: Egyéni üzleti szabályok hozzáadása a BLL osztályaihoz
A mezőszintű érvényesítés mellett lehetnek olyan magas szintű egyéni üzleti szabályok, amelyek különböző entitásokat vagy fogalmakat foglalnak magukban, amelyek nem fejezhetők ki egyetlen oszlop szintjén, például:
- Ha egy termék megszűnik, az
UnitPricenem frissíthető - A munkavállaló tartózkodási országának meg kell egyeznie a vezető tartózkodási országával
- A termék nem szüntethető meg, ha ez az egyetlen, a szállító által biztosított termék
A BLL-osztályoknak olyan ellenőrzéseket kell tartalmazniuk, amelyek biztosítják az alkalmazás üzleti szabályainak betartását. Ezek az ellenőrzések közvetlenül hozzáadhatók az általuk alkalmazott módszerekhez.
Tegyük fel, hogy az üzleti szabályok azt diktálják, hogy egy termék ne legyen megszüntetve, ha egy adott szállító egyetlen terméke volt. Vagyis ha az X termék volt az egyetlen termék, amelyet az Y szállítótól vásároltunk, akkor nem tudtuk megszüntetni az X jelölését; ha azonban az Y szállító három terméket adott nekünk, A, B és C terméket, akkor ezek bármelyikét megszüntettük. Furcsa üzleti szabály, de az üzleti szabályok és a józan ész nem mindig igazodnak egymáshoz!
Ahhoz, hogy ezt az üzleti szabályt az UpdateProducts első módszer szerint érvényesítsük, ellenőrizzük, hogy Discontinued be van-e True állítva, és ha igen, hívjuk GetProductsBySupplierID meg, hogy hány terméket vásároltunk a termék szállítójától. Ha csak egy terméket vásárolnak ettől a szállítótól, ejtünk egy 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
Válaszadás a prezentációs réteg érvényesítési hibáira
Amikor meghívjuk a BLL-t a bemutatószintről, eldönthetjük, hogy megkíséreljük-e kezelni az esetlegesen felmerülő kivételeket, vagy hagyjuk, hogy azok átadódjanak az ASP.NET-nek (ami aktiválja a HttpApplication eseményt Error). A BLL programozott használata során a kivételek kezeléséhez használhatjuk a Próbálja ki... Catch blokk , ahogy az alábbi példa is mutatja:
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
Ahogy a jövőbeli oktatóanyagokban is látni fogjuk, a BLL-ből az adatok beszúrására, frissítésére vagy törlésére szolgáló adat-webvezérlők használata során felmerülő kivételek kezelése közvetlenül egy eseménykezelőben kezelhető, nem pedig blokkokban Try...Catch lévő kódfuttatással.
Összefoglalás
Egy jól kialakított alkalmazás különböző rétegekbe van beosztva, amelyek mindegyike egy adott szerepkört foglal magában. A cikksorozat első oktatóanyagában egy adatelérési réteget hoztunk létre gépelt adathalmazok használatával; Ebben az oktatóanyagban egy üzleti logikai réteget hoztunk létre osztálysorozatként az alkalmazás mappájában App_Code , amely lehívja a DAL-unkat. A BLL implementálja az alkalmazás mezőszintű és üzleti szintű logikáját. Amellett, hogy külön BLL-t hoztunk létre, ahogyan ebben az oktatóanyagban is tettük, egy másik lehetőség a TableAdapters metódusainak kiterjesztése részleges osztályok használatával. Ennek a technikának a használata azonban nem teszi lehetővé a meglévő módszerek felülbírálását, és nem választja el a DAL-t és a BLL-t olyan tiszta módon, mint a jelen cikkben ismertetett megközelítés.
A DAL és a BLL elkészültével készen állunk arra, hogy elkezdjük a prezentációs réteggel való munkát. A következő oktatóanyagban rövid kitérőt teszünk az adathozzáférési témakörökből, és konzisztens lapelrendezést határozunk meg az oktatóanyagok során.
Boldog programozást!
Tudnivalók a szerzőről
Scott Mitchell, hét ASP/ASP.NET-könyv szerzője és a 4GuysFromRolla.com alapítója, 1998 óta dolgozik a Microsoft webtechnológiáival. Scott független tanácsadóként, edzőként és íróként dolgozik. Legújabb könyve Sams Tanuld meg ASP.NET 2.0 24 óra alatt. Ő itt elérhető mitchell@4GuysFromRolla.com.
Külön köszönet
Ezt az oktatóanyag-sorozatot sok hasznos véleményező áttekintette. Az oktatóanyag vezető véleményezői Liz Shulok, Dennis Patterson, Carlos Santos és Hilton Giesenow voltak. Szeretné áttekinteni a közelgő MSDN-cikkeimet? Ha igen, írj egy sort a mitchell@4GuysFromRolla.com-ra.