Tworzenie warstwy logiki biznesowej (VB)
W tym samouczku zobaczymy, jak scentralizować reguły biznesowe w warstwie logiki biznesowej (BLL), która służy jako pośrednik w wymianie danych między warstwą prezentacji a usługą DAL.
Wprowadzenie
Warstwa dostępu do danych (DAL) utworzona w pierwszym samouczku w sposób czysty oddziela logikę dostępu do danych od logiki prezentacji. Jednak mimo że funkcja DAL w sposób czysty oddziela szczegóły dostępu do danych od warstwy prezentacji, nie wymusza żadnych reguł biznesowych, które mogą być stosowane. Na przykład w przypadku naszej aplikacji możemy nie zezwalać CategoryID
na modyfikowanie pól Products
lub SupplierID
tabeli, gdy Discontinued
pole jest ustawione na 1, lub możemy chcieć wymusić reguły seniorstwa, zakazując sytuacji, w których pracownik jest zarządzany przez kogoś, kto został zatrudniony po nich. Innym typowym scenariuszem jest autoryzacja może być tylko użytkownicy w określonej roli mogą usuwać produkty lub zmieniać UnitPrice
wartość.
W tym samouczku zobaczymy, jak scentralizować te reguły biznesowe w warstwie logiki biznesowej (BLL), która służy jako pośrednik w wymianie danych między warstwą prezentacji a usługą DAL. W rzeczywistej aplikacji BLL należy zaimplementować jako oddzielny projekt biblioteki klas; Jednak w przypadku tych samouczków wdrożymy usługę BLL jako serię klas w naszym App_Code
folderze, aby uprościć strukturę projektu. Rysunek 1 ilustruje relacje architektury między warstwą prezentacji, BLL i DAL.
Rysunek 1. Usługa BLL oddziela warstwę prezentacji od warstwy dostępu do danych i nakłada reguły biznesowe
Zamiast tworzyć oddzielne klasy w celu zaimplementowania logiki biznesowej, możemy alternatywnie umieścić tę logikę bezpośrednio w typowym zestawie danych z klasami częściowymi. Aby zapoznać się z przykładem tworzenia i rozszerzania typu zestawu danych, zapoznaj się z pierwszym samouczkiem.
Krok 1. Tworzenie klas BLL
Nasza BLL będzie składać się z czterech klas, jeden dla każdego TableAdapter w DAL; każda z tych klas BLL będzie miała metody pobierania, wstawiania, aktualizowania i usuwania z odpowiedniej klasy TableAdapter w dal, stosując odpowiednie reguły biznesowe.
Aby dokładniej oddzielić klasy związane z dal i BLL, utwórzmy dwa podfoldery w folderze App_Code
DAL
i BLL
. Po prostu kliknij prawym przyciskiem myszy App_Code
folder w Eksplorator rozwiązań i wybierz pozycję Nowy folder. Po utworzeniu tych dwóch folderów przenieś typowy zestaw danych utworzony w pierwszym samouczku do podfolderu DAL
.
Następnie utwórz cztery pliki klas BLL w BLL
podfolderze. Aby to osiągnąć, kliknij prawym przyciskiem myszy BLL
podfolder, wybierz pozycję Dodaj nowy element, a następnie wybierz szablon Klasa. Nazwij cztery klasy ProductsBLL
, CategoriesBLL
, SuppliersBLL
i EmployeesBLL
.
Rysunek 2. Dodawanie czterech nowych klas do App_Code
folderu
Następnie dodajmy metody do każdej klasy, aby po prostu opakować metody zdefiniowane dla klasy TableAdapters z pierwszego samouczka. Na razie te metody będą po prostu wywoływać bezpośrednio do dal; Wrócimy później, aby dodać dowolną potrzebną logikę biznesową.
Uwaga
Jeśli używasz programu Visual Studio Standard Edition lub nowszego (czyli nie używasz programu Visual Web Developer), możesz opcjonalnie wizualnie zaprojektować klasy przy użyciu Projektant klasy. Aby uzyskać więcej informacji na temat tej nowej funkcji w programie Visual Studio, zapoznaj się z blogami klasy Projektant.
ProductsBLL
Dla klasy musimy dodać łącznie siedem metod:
GetProducts()
zwraca wszystkie produktyGetProductByProductID(productID)
zwraca produkt o określonym identyfikatorze produktuGetProductsByCategoryID(categoryID)
zwraca wszystkie produkty z określonej kategoriiGetProductsBySupplier(supplierID)
zwraca wszystkie produkty od określonego dostawcyAddProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued)
wstawia nowy produkt do bazy danych przy użyciu przekazanych wartości;ProductID
zwraca wartość nowo wstawionego rekorduUpdateProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued, productID)
aktualizuje istniejący produkt w bazie danych przy użyciu przekazanych wartości; zwraca wartośćTrue
, jeśli dokładnie jeden wiersz został zaktualizowany,False
w przeciwnym razieDeleteProduct(productID)
usuwa określony produkt z bazy danych
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, które po prostu zwracają dane GetProducts
, , GetProductByProductID
GetProductsByCategoryID
i GetProductBySuppliersID
są dość proste, ponieważ po prostu są wywoływane do dal. W niektórych scenariuszach mogą istnieć reguły biznesowe, które należy zaimplementować na tym poziomie (takie jak reguły autoryzacji oparte na aktualnie zalogowanym użytkowniku lub roli, do której należy użytkownik), po prostu pozostawimy te metody zgodnie z oczekiwaniami. W przypadku tych metod usługa BLL służy jedynie jako serwer proxy, za pośrednictwem którego warstwa prezentacji uzyskuje dostęp do danych bazowych z warstwy dostępu do danych.
Metody AddProduct
i UpdateProduct
przyjmują zarówno jako parametry wartości różnych pól produktu, jak i dodają nowy produkt lub zaktualizują istniejącą odpowiednio. Ponieważ wiele Product
kolumn tabeli może akceptować NULL
wartości (CategoryID
, SupplierID
i UnitPrice
, w celu nadenia kilku), te parametry wejściowe dla AddProduct
i UpdateProduct
mapowania na takie kolumny używają typów dopuszczających wartość null. Typy dopuszczające wartość null są nowe dla platformy .NET 2.0 i zapewniają technikę wskazującą, czy zamiast tego powinien być Nothing
typem wartości . Aby uzyskać więcej informacji, zapoznaj się z wpisem w blogu Paul VickThe Truth About Nullable Types and VB (Prawda o typach dopuszczanych do wartości null) oraz dokumentacją techniczną dotyczącą struktury dopuszczalnej do wartości null .
Wszystkie trzy metody zwracają wartość logiczną wskazującą, czy wiersz został wstawiony, zaktualizowany lub usunięty, ponieważ operacja może nie spowodować wystąpienia wiersza, którego dotyczy problem. Jeśli na przykład deweloper strony wywołuje DeleteProduct
przekazywanie elementu ProductID
dla nieistniejącego produktu, DELETE
instrukcja wystawiona dla bazy danych nie będzie miała wpływu, a zatem DeleteProduct
metoda zwróci False
wartość .
Należy pamiętać, że podczas dodawania nowego produktu lub aktualizowania istniejącego elementu przyjmujemy wartości pól nowego lub zmodyfikowanego produktu jako listę skalarnych, a nie akceptowanie ProductsRow
wystąpienia. Ta metoda została wybrana, ponieważ ProductsRow
klasa pochodzi z klasy ADO.NETDataRow
, która nie ma domyślnego konstruktora bez parametrów. Aby utworzyć nowe ProductsRow
wystąpienie, musimy najpierw utworzyć wystąpienie, a następnie wywołać jego NewProductRow()
metodę ProductsDataTable
(co robimy w AddProduct
programie ). To krótkie tyły głowy, gdy zwrócimy się do wstawiania i aktualizowania produktów przy użyciu obiektu ObjectDataSource. Krótko mówiąc, obiekt ObjectDataSource spróbuje utworzyć wystąpienie parametrów wejściowych. Jeśli metoda BLL oczekuje wystąpienia, obiekt ObjectDataSource spróbuje ProductsRow
go utworzyć, ale nie powiedzie się z powodu braku domyślnego konstruktora bez parametrów. Aby uzyskać więcej informacji na temat tego problemu, zapoznaj się z następującymi dwoma wpisami na forach ASP.NET: Aktualizowanie obiektówDataSources przy użyciu zestawów danych Strongly-Typedoraz Problem z obiektami ObjectDataSource i Strongly-Typed DataSet.
Następnie w obu przypadkach AddProduct
UpdateProduct
kod tworzy ProductsRow
wystąpienie i wypełnia je wartościami, które właśnie zostały przekazane. Podczas przypisywania wartości do kolumny DataColumns obiektu DataRow mogą wystąpić różne kontrole poprawności na poziomie pola. W związku z tym ręczne umieszczenie przekazanych wartości z powrotem do elementu DataRow pomaga zapewnić ważność danych przekazywanych do metody BLL. Niestety silnie typizowane klasy DataRow wygenerowane przez program Visual Studio nie używają typów dopuszczalnych wartości null. Aby wskazać, że określona kolumna DataColumn w elemecie DataRow powinna odpowiadać NULL
wartości bazy danych, należy użyć SetColumnNameNull()
metody .
W UpdateProduct
pliku najpierw załadujemy produkt do aktualizacji przy użyciu polecenia GetProductByProductID(productID)
. Chociaż może to wydawać się niepotrzebną podróżą do bazy danych, ta dodatkowa podróż okaże się warta w przyszłych samouczkach, które eksplorują optymistyczną współbieżność. Optymistyczna współbieżność to technika zapewniająca, że dwóch użytkowników, którzy pracują jednocześnie nad tymi samymi danymi, nie zastępuje przypadkowo zmian. Pobranie całego rekordu ułatwia również tworzenie metod aktualizacji w usłudze BLL, które modyfikują tylko podzestaw kolumn DataRow. Podczas eksplorowania SuppliersBLL
klasy zobaczymy taki przykład.
Na koniec należy pamiętać, że ProductsBLL
klasa ma zastosowany atrybut DataObject ( [System.ComponentModel.DataObject]
składnia bezpośrednio przed instrukcją klasy w górnej części pliku) i metody mają atrybuty DataObjectMethodAttribute. Atrybut DataObject
oznacza klasę jako obiekt odpowiedni do powiązania z kontrolką ObjectDataSource, natomiast DataObjectMethodAttribute
element wskazuje przeznaczenie metody. Jak zobaczymy w przyszłych samouczkach, ASP.NET 2.0 ObjectDataSource ułatwia deklaratywne uzyskiwanie dostępu do danych z klasy. Aby ułatwić filtrowanie listy możliwych klas do powiązania z kreatorem objectDataSource, domyślnie tylko te klasy oznaczone jako DataObjects
są wyświetlane na liście rozwijanej kreatora. Klasa ProductsBLL
będzie działać równie dobrze bez tych atrybutów, ale dodanie ich ułatwia pracę z kreatorem ObjectDataSource.
Dodawanie innych klas
ProductsBLL
Po ukończeniu klasy nadal musimy dodać klasy do pracy z kategoriami, dostawcami i pracownikami. Poświęć chwilę, aby utworzyć następujące klasy i metody przy użyciu pojęć przedstawionych w powyższym przykładzie:
CategoriesBLL.cs
GetCategories()
GetCategoryByCategoryID(categoryID)
SuppliersBLL.cs
GetSuppliers()
GetSupplierBySupplierID(supplierID)
GetSuppliersByCountry(country)
UpdateSupplierAddress(supplierID, address, city, country)
EmployeesBLL.cs
GetEmployees()
GetEmployeeByEmployeeID(employeeID)
GetEmployeesByManager(managerID)
Jedną z metod, których warto zauważyć, jest SuppliersBLL
metoda klasy UpdateSupplierAddress
. Ta metoda udostępnia interfejs umożliwiający aktualizowanie tylko informacji o adresach dostawcy. Wewnętrznie ta metoda odczytuje obiekt dla określonego SupplierDataRow
supplierID
(przy użyciu GetSupplierBySupplierID
), ustawia jego właściwości związane z adresami, a następnie wywołuje w dół do SupplierDataTable
metody "s Update
". Metoda jest następująca UpdateSupplierAddress
:
<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
Zapoznaj się z pobieraniem tego artykułu, aby uzyskać pełną implementację klas BLL.
Krok 2. Uzyskiwanie dostępu do typowych zestawów danych za pośrednictwem klas BLL
W pierwszym samouczku widzieliśmy przykłady pracy bezpośrednio z typed DataSet programowo, ale z dodatkiem naszych klas BLL warstwa prezentacji powinna działać w stosunku do BLL. W przykładzie AllProducts.aspx
z pierwszego samouczka ProductsTableAdapter
użyto narzędzia do powiązania listy produktów z elementem GridView, jak pokazano w poniższym kodzie:
Dim productsAdapter As New ProductsTableAdapter()
GridView1.DataSource = productsAdapter.GetProducts()
GridView1.DataBind()
Aby użyć nowych klas BLL, wszystko, co należy zmienić, to pierwszy wiersz kodu po prostu zastąpić ProductsTableAdapter
obiekt obiektem ProductBLL
:
Dim productLogic As New ProductsBLL()
GridView1.DataSource = productLogic.GetProducts()
GridView1.DataBind()
Do klas BLL można również uzyskać dostęp deklaratywnie (co może być typed DataSet) przy użyciu obiektu ObjectDataSource. Bardziej szczegółowo omówimy obiekt ObjectDataSource w poniższych samouczkach.
Rysunek 3. Lista produktów jest wyświetlana w widoku GridView (kliknij, aby wyświetlić obraz pełnowymiarowy)
Krok 3. Dodawanie weryfikacji Field-Level do klas DataRow
Sprawdzanie poprawności na poziomie pola dotyczy wartości właściwości obiektów biznesowych podczas wstawiania lub aktualizowania. Niektóre reguły sprawdzania poprawności na poziomie pola dla produktów obejmują:
- Pole
ProductName
musi mieć długość 40 znaków lub mniej - Pole
QuantityPerUnit
musi mieć długość 20 znaków lub mniej - Pola
ProductID
,ProductName
iDiscontinued
są wymagane, ale wszystkie inne pola są opcjonalne - Pola
UnitPrice
,UnitsInStock
,UnitsOnOrder
iReorderLevel
muszą być większe niż lub równe zero
Te reguły mogą i powinny być wyrażone na poziomie bazy danych. Limit znaków w ProductName
polach i QuantityPerUnit
są przechwytywane przez typy danych tych kolumn w Products
tabeli (nvarchar(40)
i nvarchar(20)
, odpowiednio). Określa, czy pola są wymagane, a opcjonalne są wyrażane przez kolumnę tabeli NULL
bazy danych. Istnieją cztery ograniczenia sprawdzania, które zapewniają, że tylko wartości większe niż lub równe zero mogą przekształcić je w UnitPrice
kolumny , , UnitsInStock
lub UnitsOnOrder
ReorderLevel
.
Oprócz wymuszania tych reguł w bazie danych powinny być również wymuszane na poziomie zestawu danych. W rzeczywistości długość pola i to, czy wartość jest wymagana, czy opcjonalna, są już przechwytywane dla zestawu kolumn DataColumns każdej tabeli danych. Aby automatycznie wyświetlić istniejącą weryfikację na poziomie pola, przejdź do Projektant Zestawu danych, wybierz pole z jednej z tabel danych, a następnie przejdź do okno Właściwości. Jak pokazano na rysunku 4, kolumna QuantityPerUnit
DataColumn w obiekcie ProductsDataTable
ma maksymalną długość 20 znaków i zezwala na NULL
wartości. Jeśli spróbujemy ustawić ProductsDataRow
właściwość "s QuantityPerUnit
" na wartość ciągu dłuższą niż 20 znaków, ArgumentException
zostanie zgłoszony.
Rysunek 4. Kolumna danych zapewnia podstawową weryfikację Field-Level (kliknij, aby wyświetlić obraz pełnowymiarowy)
Niestety, nie można określić kontroli granic, takich jak UnitPrice
wartość musi być większa lub równa zero, za pośrednictwem okno Właściwości. Aby zapewnić ten typ weryfikacji na poziomie pola, musimy utworzyć procedurę obsługi zdarzeń dla zdarzenia ColumnChanging tabeli Danych. Jak wspomniano w poprzednim samouczku, obiekty DataSet, DataTables i DataRow utworzone przez typowy zestaw danych można rozszerzyć za pomocą klas częściowych. Korzystając z tej techniki, można utworzyć procedurę ColumnChanging
obsługi zdarzeń dla ProductsDataTable
klasy. Zacznij od utworzenia klasy w folderze App_Code
o nazwie ProductsDataTable.ColumnChanging.vb
.
Rysunek 5. Dodawanie nowej klasy do App_Code
folderu (kliknij, aby wyświetlić obraz pełnowymiarowy)
Następnie utwórz procedurę obsługi zdarzeń dla ColumnChanging
zdarzenia, która gwarantuje, że UnitPrice
wartości kolumn , , UnitsInStock
UnitsOnOrder
i ReorderLevel
(jeśli nie NULL
) są większe lub równe zero. Jeśli jakakolwiek taka kolumna jest poza zakresem, wyrzuć wartość 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. Dodawanie niestandardowych reguł biznesowych do klas BLL
Oprócz sprawdzania poprawności na poziomie pola mogą istnieć niestandardowe reguły biznesowe wysokiego poziomu, które obejmują różne jednostki lub pojęcia, które nie są wyrażalne na poziomie jednej kolumny, takie jak:
- Jeśli produkt zostanie przerwany, nie można go
UnitPrice
zaktualizować - Kraj zamieszkania pracownika musi być taki sam jak kraj zamieszkania swojego menedżera
- Nie można wycofać produktu, jeśli jest to jedyny produkt dostarczony przez dostawcę
Klasy BLL powinny zawierać kontrole w celu zapewnienia zgodności z regułami biznesowymi aplikacji. Te kontrole można dodać bezpośrednio do metod, do których mają zastosowanie.
Załóżmy, że nasze zasady biznesowe określają, że produkt nie może być oznaczony jako przerwany, jeśli był to jedyny produkt od danego dostawcy. Oznacza to, że jeśli produkt X był jedynym produktem zakupionym od dostawcy Y, nie mogliśmy oznaczyć X jako przerwanego; jeśli jednak dostawca Y dostarczył nam trzy produkty, A, B i C, możemy oznaczyć dowolne i wszystkie z nich jako przerwane. Dziwna reguła biznesowa, ale reguły biznesowe i zdrowy rozsądek nie zawsze są zgodne!
Aby wymusić tę regułę biznesową UpdateProducts
w metodzie, zaczniemy od sprawdzenia, czy Discontinued
została ustawiona wartość True
i, jeśli tak, wywołamy metodę GetProductsBySupplierID
określania liczby produktów zakupionych od dostawcy tego produktu. W przypadku zakupu tylko jednego produktu od tego dostawcy zgłaszamy wyjątek 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
Odpowiadanie na błędy walidacji w warstwie prezentacji
Podczas wywoływania biblioteki BLL z warstwy prezentacji możemy zdecydować, czy należy podjąć próbę obsługi wszelkich wyjątków, które mogą zostać podniesione, czy pozwolić im utworzyć bąbelek do ASP.NET (co spowoduje podniesienie HttpApplication
Error
zdarzenia). Aby obsłużyć wyjątek podczas pracy z BLL programowo, możemy użyć try... Blok catch , jak pokazano w poniższym przykładzie:
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 zobaczymy w przyszłych samouczkach, obsługa wyjątków, które są bąbelkowe z poziomu BLL podczas korzystania z kontrolki internetowej danych do wstawiania, aktualizowania lub usuwania danych, można obsłużyć bezpośrednio w procedurze obsługi zdarzeń, w przeciwieństwie do konieczności zawijania kodu w Try...Catch
blokach.
Podsumowanie
Dobrze zaprojektowana aplikacja jest spreparowana na różne warstwy, z których każda hermetyzuje określoną rolę. W pierwszym samouczku tej serii artykułów utworzyliśmy warstwę dostępu do danych przy użyciu typowych zestawów danych; W tym samouczku utworzyliśmy warstwę logiki biznesowej jako serię klas w folderze naszej aplikacji App_Code
, które wywołają usługę DAL. Usługa BLL implementuje logikę na poziomie pola i na poziomie biznesowym dla naszej aplikacji. Oprócz tworzenia oddzielnej BLL, podobnie jak w tym samouczku, inną opcją jest rozszerzenie metod TableAdapters przy użyciu klas częściowych. Jednak użycie tej techniki nie pozwala nam zastąpić istniejących metod ani oddzielić naszego dal i naszego BLL tak czysto, jak podejście podjęte w tym artykule.
Po zakończeniu dal i BLL możemy rozpocząć pracę nad naszą warstwą prezentacji. W następnym samouczku omówimy krótki przewodnik od tematów dostępu do danych i zdefiniujemy spójny układ strony do użycia w ramach samouczków.
Szczęśliwe programowanie!
Informacje o autorze
Scott Mitchell, autor siedmiu książek ASP/ASP.NET i założyciel 4GuysFromRolla.com, współpracuje z technologiami internetowymi firmy Microsoft od 1998 roku. Scott pracuje jako niezależny konsultant, trener i pisarz. Jego najnowsza książka to Sams Teach Yourself ASP.NET 2.0 w ciągu 24 godzin. Można do niego dotrzeć pod adresem mitchell@4GuysFromRolla.com. Lub za pośrednictwem swojego bloga, który można znaleźć na stronie http://ScottOnWriting.NET.
Specjalne podziękowania
Ta seria samouczków została sprawdzona przez wielu pomocnych recenzentów. Recenzenci w tym samouczku byli Liz Shulok, Dennis Patterson, Carlos Santos i Hilton Giesenow. Chcesz przejrzeć nadchodzące artykuły MSDN? Jeśli tak, upuść mi wiersz pod adresem mitchell@4GuysFromRolla.com.