Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
door Scott Mitchell
In deze zelfstudie leert u hoe u uw bedrijfsregels kunt centraliseren in een BLL (Business Logic Layer) die fungeert als intermediair voor gegevensuitwisseling tussen de presentatielaag en de DAL.
Introductie
De Data Access Layer (DAL) die in de eerste zelfstudie is gemaakt, scheidt de logica voor gegevenstoegang op een schone manier van de presentatielogica. Hoewel de DAL de gegevenstoegangsgegevens van de presentatielaag op schone wijze scheidt, worden er geen bedrijfsregels afgedwongen die van toepassing kunnen zijn. Voor onze toepassing willen we bijvoorbeeld het wijzigen van de CategoryID velden van de SupplierID tabel weigeren wanneer het Products veld is ingesteld op 1, of misschien willen we senioriteitsregels afdwingen, waardoor situaties worden verboden waarin een werknemer wordt beheerd door iemand die erna is ingehuurdDiscontinued. Een ander veelvoorkomend scenario is autorisatie mogelijk dat alleen gebruikers met een bepaalde rol producten kunnen verwijderen of de UnitPrice waarde kunnen wijzigen.
In deze zelfstudie zien we hoe u deze bedrijfsregels kunt centraliseren in een BLL (Business Logic Layer) die fungeert als intermediair voor gegevensuitwisseling tussen de presentatielaag en de DAL. In een echte toepassing moet de BLL worden geïmplementeerd als een afzonderlijk klassebibliotheekproject; Voor deze zelfstudies implementeren we de BLL echter als een reeks klassen in onze App_Code map om de projectstructuur te vereenvoudigen. Afbeelding 1 illustreert de architectuurrelaties tussen de presentatielaag, BLL en DAL.
Afbeelding 1: De BLL scheidt de presentatielaag van de Gegevenstoegangslaag en legt bedrijfsregels op
In plaats van afzonderlijke klassen te maken om onze bedrijfslogica te implementeren, kunnen we deze logica ook rechtstreeks in de getypte gegevensset plaatsen met gedeeltelijke klassen. Raadpleeg de eerste handleiding voor een voorbeeld van het maken en uitbreiden van een gegeformatteerde gegevensset.
Stap 1: De BLL-klassen maken
Onze BLL bestaat uit vier klassen, één voor elke TableAdapter in de DAL; elk van deze BLL-klassen heeft methoden voor het ophalen, invoegen, bijwerken en verwijderen van de respectieve TableAdapter in de DAL, waarbij de juiste bedrijfsregels worden toegepast.
Als u de DAL- en BLL-gerelateerde klassen beter wilt scheiden, maken we twee submappen in de App_Code map DAL en BLL. Klik met de rechtermuisknop op de App_Code map in Solution Explorer en kies Nieuwe map. Nadat u deze twee mappen hebt gemaakt, verplaatst u de getypte gegevensset die u in de eerste zelfstudie hebt gemaakt naar de DAL submap.
Maak vervolgens de vier BLL-klassebestanden in de BLL submap. Hiervoor klikt u met de rechtermuisknop op de BLL submap, kiest u Een nieuw item toevoegen en kiest u de klassesjabloon. Noem de vier klassen ProductsBLL, CategoriesBLLen SuppliersBLLEmployeesBLL.
Afbeelding 2: Vier nieuwe klassen toevoegen aan de App_Code map
Vervolgens gaan we methoden toevoegen aan elk van de klassen om eenvoudig de methoden te verpakken die zijn gedefinieerd voor tableAdapters uit de eerste zelfstudie. Op dit moment zullen deze methoden gewoon rechtstreeks in de DAL worden aangeroepen; We keren later terug om eventuele benodigde bedrijfslogica toe te voegen.
Opmerking
Als u Visual Studio Standard Edition of hoger gebruikt (u gebruikt dus geen Visual Web Developer), kunt u eventueel uw klassen visueel ontwerpen met class Designer. Raadpleeg het blog Class Designer voor meer informatie over deze nieuwe functie in Visual Studio.
Voor de ProductsBLL klasse moeten we in totaal zeven methoden toevoegen:
-
GetProducts()retourneert alle producten -
GetProductByProductID(productID)retourneert het product met de opgegeven product-id -
GetProductsByCategoryID(categoryID)retourneert alle producten uit de opgegeven categorie -
GetProductsBySupplier(supplierID)retourneert alle producten van de opgegeven leverancier -
AddProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued)voegt een nieuw product in de database in met behulp van de waarden die zijn doorgegeven; retourneert deProductIDwaarde van de nieuw ingevoegde record -
UpdateProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued, productID)werkt een bestaand product in de database bij met behulp van de doorgegeven waarden; retourneertTrueals precies één rij is bijgewerkt,Falseanders -
DeleteProduct(productID)verwijdert het opgegeven product uit de database
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
De methoden die simpelweg gegevens GetProductsretourneren, GetProductByProductIDGetProductsByCategoryIDen GetProductBySuppliersID zijn vrij eenvoudig omdat ze simpelweg in de DAL worden aangeroepen. In sommige scenario's kunnen er bedrijfsregels zijn die op dit niveau moeten worden geïmplementeerd (zoals autorisatieregels op basis van de momenteel aangemelde gebruiker of de rol waartoe de gebruiker behoort), laten we deze methoden gewoon as-is. Voor deze methoden dient de BLL dan alleen als een proxy waarmee de presentatielaag toegang heeft tot de onderliggende gegevens uit de Data Access-laag.
De AddProduct en UpdateProduct methoden nemen beide op als parameters de waarden voor de verschillende productvelden en voegen een nieuw product toe of werken een bestaand product bij. Omdat veel van de kolommen van de Product tabel waarden kunnen accepteren NULL (CategoryIDSupplierIDenUnitPrice, om er een paar te noemen), gebruiken die invoerparameters voor AddProduct en UpdateProduct die worden toegewezen aan dergelijke kolommen null-typen. Null-typen zijn nieuw voor .NET 2.0 en bieden een techniek om aan te geven of een waardetype in plaats daarvan moet zijn Nothing. Raadpleeg het blogbericht van Paul VickThe Truth About Nullable Types en VB en de technische documentatie voor de Nullable-structuur voor meer informatie.
Alle drie de methoden retourneren een Booleaanse waarde die aangeeft of een rij is ingevoegd, bijgewerkt of verwijderd, omdat de bewerking mogelijk niet resulteert in een betrokken rij. Als de paginaontwikkelaar bijvoorbeeld DeleteProduct aanroept met een ProductID voor een niet-bestaand product, zal de DELETE-instructie die aan de database is uitgegeven geen effect hebben en zal de DeleteProduct-methode False retourneren.
Houd er rekening mee dat wanneer u een nieuw product toevoegt of een bestaand product bijwerkt, de veldwaarden van het nieuwe of gewijzigde product worden opgenomen als een lijst met scalaire waarden in plaats van een ProductsRow exemplaar te accepteren. Deze methode is gekozen omdat de ProductsRow klasse is afgeleid van de ADO.NET-klasse DataRow , die geen standaardconstructor zonder parameter heeft. Om een nieuw ProductsRow exemplaar te maken, moeten we eerst een ProductsDataTable exemplaar maken en vervolgens de NewProductRow() methode aanroepen (wat we doen).AddProduct Deze tekortkoming komt naar voren wanneer we producten invoegen en bijwerken met behulp van de ObjectDataSource. Kortom, de ObjectDataSource probeert een exemplaar van de invoerparameters te maken. Als de BLL-methode een ProductsRow exemplaar verwacht, probeert de ObjectDataSource er een te maken, maar mislukt deze vanwege het ontbreken van een standaardconstructor zonder parameter. Raadpleeg de volgende twee ASP.NET Forums-berichten voor meer informatie over dit probleem: ObjectDataSources bijwerken met Strongly-Typed DataSets en Probleem met ObjectDataSource en Strongly-Typed DataSet.
Vervolgens wordt in beide AddProduct en UpdateProduct, de code een ProductsRow instantie gemaakt en gevuld met de waarden die zojuist zijn doorgegeven. Bij het toewijzen van waarden aan DataColumns van een DataRow kunnen verschillende validatiecontroles op veldniveau plaatsvinden. Het handmatig terugzetten van de doorgegeven waarden in een DataRow helpt de geldigheid van de gegevens te waarborgen die naar de BLL-methode worden doorgegeven. Helaas gebruiken de sterk getypte DataRow-klassen die door Visual Studio worden gegenereerd geen nullbare typen. Om aan te geven dat een bepaalde DataColumn in een DataRow moet overeenkomen met een NULL databasewaarde, moeten we de SetColumnNameNull() methode gebruiken.
In UpdateProduct laden we eerst het product dat moet worden bijgewerkt met behulp van GetProductByProductID(productID). Hoewel dit misschien een onnodige reis naar de database lijkt, zal deze extra reis de moeite waard blijken in toekomstige zelfstudies die optimistische gelijktijdigheid verkennen. Optimistische gelijktijdigheid is een techniek om ervoor te zorgen dat twee gebruikers die tegelijkertijd aan dezelfde gegevens werken, niet per ongeluk de wijzigingen van elkaar overschrijven. Door de hele record te pakken, is het ook eenvoudiger om updatemethoden te maken in de BLL die alleen een subset van de kolommen van DataRow wijzigen. Wanneer we de SuppliersBLL klas verkennen, zien we zo'n voorbeeld.
Houd er ten slotte rekening mee dat voor de ProductsBLL klasse het DataObject-kenmerk is toegepast (de [System.ComponentModel.DataObject] syntaxis vlak vóór de klasse-instructie boven aan het bestand) en dat de methoden DataObjectMethodAttribute-kenmerken hebben. Het DataObject kenmerk markeert de klasse als een object dat geschikt is voor binding met een ObjectDataSource-besturingselement, terwijl het DataObjectMethodAttribute doel van de methode aangeeft. Zoals we in toekomstige zelfstudies zien, maakt ASP.NET ObjectDataSource van 2.0 het eenvoudig om gegevens van een klasse declaratief te openen. Om te helpen de lijst met mogelijke klassen om aan te binden in de wizard ObjectDataSource te filteren, worden standaard alleen de klassen weergegeven die als DataObjects gemarkeerd zijn in de vervolgkeuzelijst van de wizard. De ProductsBLL klasse werkt net zo goed als zonder deze kenmerken, maar als u ze toevoegt, is het eenvoudiger om met de wizard ObjectDataSource te werken.
De andere klassen toevoegen
Nu de ProductsBLL klas is voltooid, moeten we nog steeds de klassen toevoegen voor het werken met categorieën, leveranciers en werknemers. Neem even de tijd om de volgende klassen en methoden te maken met behulp van de concepten uit het bovenstaande voorbeeld:
CategoriesBLL.cs
GetCategories()GetCategoryByCategoryID(categoryID)
SuppliersBLL.cs
GetSuppliers()GetSupplierBySupplierID(supplierID)GetSuppliersByCountry(country)UpdateSupplierAddress(supplierID, address, city, country)
EmployeesBLL.cs
GetEmployees()GetEmployeeByEmployeeID(employeeID)GetEmployeesByManager(managerID)
Een methode die het vermelden waard is, is de SuppliersBLL klasse's UpdateSupplierAddress methode. Deze methode biedt een interface voor het bijwerken van alleen de adresgegevens van de leverancier. Intern werkt deze methode het SupplierDataRow object voor het opgegeven supplierID in (met behulp van GetSupplierBySupplierID), stelt het de adresgerelateerde eigenschappen in en roept vervolgens de SupplierDataTable-methode van Update aan. De UpdateSupplierAddress methode volgt:
<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
Raadpleeg de download van dit artikel voor mijn volledige implementatie van de BLL-klassen.
Stap 2: Toegang tot de getypte gegevenssets via de BLL-klassen
In de eerste zelfstudie hebben we voorbeelden gezien van het programmatisch werken met de Getypte DataSet, maar met de toevoeging van onze BLL-klassen moet de presentatielaag in plaats daarvan tegen de BLL werken. In het AllProducts.aspx voorbeeld uit de eerste zelfstudie is de ProductsTableAdapter lijst met producten gekoppeld aan een GridView, zoals wordt weergegeven in de volgende code:
Dim productsAdapter As New ProductsTableAdapter()
GridView1.DataSource = productsAdapter.GetProducts()
GridView1.DataBind()
Als u de nieuwe BLL-klassen wilt gebruiken, hoeft u alleen maar de eerste regel code te vervangen door ProductsTableAdapter een ProductBLL object:
Dim productLogic As New ProductsBLL()
GridView1.DataSource = productLogic.GetProducts()
GridView1.DataBind()
De BLL-klassen kunnen ook declaratief worden geopend (net als de getypte gegevensset) met behulp van de ObjectDataSource. In de volgende zelfstudies bespreken we de ObjectDataSource in meer detail.
Afbeelding 3: De lijst met producten wordt weergegeven in een rasterweergave (klik om de afbeelding op volledige grootte weer te geven)
Stap 3: Field-Level-validatie toevoegen aan de DataRow-klassen
Validatie op veldniveau is controles die betrekking hebben op de eigenschapswaarden van de bedrijfsobjecten bij het invoegen of bijwerken. Enkele validatieregels op veldniveau voor producten zijn:
- Het
ProductNameveld moet 40 tekens of minder lang zijn - Het
QuantityPerUnitveld moet 20 tekens of minder lang zijn - De
ProductIDvelden ,ProductNameenDiscontinuedvelden zijn vereist, maar alle andere velden zijn optioneel - De
UnitPricevelden ,UnitsInStockenUnitsOnOrderReorderLevelvelden moeten groter zijn dan of gelijk zijn aan nul
Deze regels kunnen en moeten worden uitgedrukt op databaseniveau. De tekenlimiet voor de ProductName en QuantityPerUnit velden worden vastgelegd door de gegevenstypen van die kolommen in de Products tabel (nvarchar(40) en nvarchar(20)respectievelijk). Of velden verplicht of optioneel zijn, komt tot uiting wanneer de databasetabelkolom NULL toestaat. Er zijn vier controlebeperkingen die ervoor zorgen dat alleen waarden groter dan of gelijk aan nul kunnen worden opgeslagen in de UnitPrice, UnitsInStock, UnitsOnOrder of ReorderLevel kolommen.
Naast het afdwingen van deze regels in de database, moeten ze ook worden afgedwongen op datasetniveau. De veldlengte en of een waarde vereist of optioneel is, worden al vastgelegd voor de set DataColumns van elke gegevenstabel. Als u de bestaande validatie op veldniveau automatisch wilt zien, gaat u naar DataSet Designer, selecteert u een veld in een van de gegevenstabellen en gaat u vervolgens naar het venster Eigenschappen. Zoals in afbeelding 4 wordt weergegeven, heeft de QuantityPerUnit DataColumn in de ProductsDataTable een maximale lengte van 20 tekens en staat het NULL waarden toe. Als we proberen de ProductsDataRow-eigenschap van QuantityPerUnit in te stellen op een tekenreeks die langer is dan 20 tekens, wordt er een ArgumentException opgegooid.
Afbeelding 4: De DataColumn biedt basisvalidatie Field-Level (klik om de afbeelding op volledige grootte weer te geven)
Helaas kunnen we geen grenscontroles opgeven, zoals dat de UnitPrice waarde groter dan of gelijk aan nul moet zijn, via het Eigenschappen venster. Als u dit type validatie op veldniveau wilt opgeven, moet u een gebeurtenis-handler maken voor de gebeurtenis ColumnChanging van de DataTable. Zoals vermeld in de voorgaande zelfstudie, kunnen de DataSet-, DataTables- en DataRow-objecten die door de Getypte DataSet zijn gemaakt, worden uitgebreid met behulp van gedeeltelijke klassen. Met deze techniek kunnen we een ColumnChanging gebeurtenis-handler voor de ProductsDataTable klasse maken. Begin met het maken van een klasse in de map met de App_Code naam ProductsDataTable.ColumnChanging.vb.
Afbeelding 5: Een nieuwe klasse toevoegen aan de App_Code map (klik om de afbeelding op volledige grootte weer te geven)
Maak vervolgens een gebeurtenis-handler voor de ColumnChanging gebeurtenis die ervoor zorgt dat de UnitPrice, UnitsInStock, UnitsOnOrder, en ReorderLevel kolomwaarden (indien niet NULL) groter dan of gelijk aan nul zijn. Als een dergelijke kolom buiten het bereik valt, gooit u een 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
Stap 4: Aangepaste bedrijfsregels toevoegen aan de klassen van BLL
Naast validatie op veldniveau zijn er mogelijk aangepaste bedrijfsregels op hoog niveau waarbij verschillende entiteiten of concepten niet worden weergegeven op één kolomniveau, zoals:
- Als een product wordt stopgezet, kan het
UnitPriceniet worden bijgewerkt - Het land van verblijf van een werknemer moet hetzelfde zijn als het land van verblijf van de manager
- Een product kan niet worden stopgezet als het het enige product is dat door de leverancier wordt geleverd
De BLL-klassen moeten controles bevatten om ervoor te zorgen dat de bedrijfsregels van de toepassing worden nageleefd. Deze controles kunnen rechtstreeks worden toegevoegd aan de methoden waarop ze van toepassing zijn.
Stel dat onze bedrijfsregels bepalen dat een product niet als beëindigd kan worden gemarkeerd als het het enige product van een bepaalde leverancier is. Als product X het enige product was dat we bij leverancier Y hebben gekocht, konden we X niet markeren als stopgezet; Als leverancier Y ons echter drie producten, A, B en C heeft geleverd, kunnen we deze als stopgezet markeren. Een oneven bedrijfsregel, maar bedrijfsregels en gezond verstand zijn niet altijd uitgelijnd!
Als we deze bedrijfsregel willen afdwingen in de UpdateProducts-methode, controleren we eerst of Discontinued is ingesteld op True en zo ja, roepen we GetProductsBySupplierID aan om te bepalen hoeveel producten we hebben gekocht bij de leverancier van het product. Als er slechts één product wordt gekocht bij deze leverancier, gooien we een 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
Reageren op validatiefouten in de presentatielaag
Wanneer u de BLL aanroept vanuit de presentatielaag, kunnen we beslissen of we eventuele opgegooide uitzonderingen proberen af te handelen of dat we ze naar ASP.NET laten doorbubbelen (waardoor het HttpApplication-gebeurtenis van Error wordt gegenereerd). Om een uitzondering af te handelen wanneer we programmatisch met de BLL werken, kunnen we een Try...Catch blok gebruiken, zoals het volgende voorbeeld laat zien.
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
Zoals we in toekomstige zelfstudies zullen zien, kunnen uitzonderingen die uit de BLL worden doorgestuurd bij het gebruik van een webbesturingselement voor het invoegen, bijwerken of verwijderen van gegevens, rechtstreeks in een gebeurtenishandler worden verwerkt in plaats van dat code in Try...Catch blokken moet worden verpakt.
Samenvatting
Een goed ontworpen toepassing is ontworpen in verschillende lagen, die elk een bepaalde rol inkapselen. In de eerste zelfstudie van deze artikelenreeks hebben we een Data Access-laag gecreëerd met behulp van getypeerde gegevenssets. In deze zelfstudie hebben we een bedrijfslogische laag gebouwd als een reeks klassen in de map App_Code van onze toepassing, die naar onze DLA verwijzen. De BLL implementeert de logica op veldniveau en bedrijfsniveau voor onze toepassing. Naast het maken van een afzonderlijke BLL, zoals we in deze zelfstudie hebben gedaan, is een andere optie om de methoden van TableAdapters uit te breiden via het gebruik van gedeeltelijke klassen. Door deze techniek te gebruiken, kunnen we echter geen bestaande methoden overschrijven en kunnen we ook onze DAL en BLL niet zo helder scheiden als met de benadering die we in dit artikel hebben gebruikt.
Nu de DAL en BLL zijn voltooid, zijn we klaar om aan de slag te gaan met onze presentatielaag. In de volgende zelfstudie volgen we een korte omleiding van onderwerpen over Data Access en definiëren we een consistente pagina-indeling voor gebruik in de zelfstudies.
Veel plezier met programmeren!
Over de auteur
Scott Mitchell, auteur van zeven ASP/ASP.NET-boeken en oprichter van 4GuysFromRolla.com, werkt sinds 1998 met Microsoft-webtechnologieën. Scott werkt als onafhankelijk consultant, trainer en schrijver. Zijn laatste boek is Sams Teach Yourself ASP.NET 2.0 in 24 uur. Hij kan worden bereikt op mitchell@4GuysFromRolla.com.
Speciale dank aan
Deze tutorialreeks is beoordeeld door veel behulpzame beoordelers. Hoofdbeoordelaars voor deze zelfstudie waren Liz Shulok, Dennis Patterson, Carlos Santos en Hilton Giesenow. Bent u geïnteresseerd in het bekijken van mijn aanstaande MSDN-artikelen? Zo ja, laat iets van je horen via mitchell@4GuysFromRolla.com.