共用方式為


建立商業邏輯層 (VB)

斯科特·米切爾

下載 PDF

在本教學課程中,我們將瞭解如何將商務規則集中至商業規則層 (BLL),以作為表示層與 DAL 之間數據交換的媒介。

簡介

在第 一個教學課程 中建立的數據存取層 (DAL) 會清楚分隔數據存取邏輯與表示邏輯。 不過,雖然 DAL 清楚地將資料存取的細節與表示層分隔開來,但並不會套用任何可能的商業規則。 例如,針對我們的應用程式,我們可能想要禁止修改CategoryID資料表的SupplierIDProducts欄位,當Discontinued欄位設定為1時,或者我們可能想要強制執行資歷規則,禁止員工被比他們晚受聘的人管理的情況。 另一個常見案例是授權,或許只有特定角色的使用者可以刪除產品或變更 UnitPrice 值。

在本教學課程中,我們將瞭解如何將這些商務規則集中到商業規則層(BLL),作為表示層與 DAL 之間數據交換的媒介。 在真實世界中,BLL 應該實作為個別的類別庫專案;不過,針對這些教學課程,我們將實作 BLL 作為資料夾中 App_Code 的一系列類別,以簡化項目結構。 圖 1 說明表示層、BLL 和 DAL 之間的架構關聯性。

BLL 會將表示層與數據存取層分開,並強制執行商務規則

圖 1:BLL 會將表示層與數據存取層分開,並強加商務規則

與其建立個別類別來實作 商業規則,不如直接將此邏輯放在具有部分類別的具型別 DataSet 中。 如需建立和擴充具型別數據集的範例,請參閱第一個教學課程。

步驟 1:建立 BLL 類別

我們的 BLL 將由四個類別組成,其中一個用於 DAL 中的每個 TableAdapter;每一個 BLL 類別都會有方法可從 DAL 中的個別 TableAdapter 擷取、插入、更新和刪除,並套用適當的商務規則。

為了更清楚地分隔 DAL 和 BLL 相關類別,讓我們在 App_Code 資料夾中建立兩個子資料夾:DALBLL。 只要以滑鼠右鍵按兩下 App_Code [方案總管] 中的資料夾,然後選擇 [新增資料夾]。 建立這兩個資料夾之後,請將第一個教學課程中建立的 DAL 具類型數據集移至子資料夾。

接下來,在 BLL 子資料夾中建立四個 BLL 類別檔案。 若要達成此目的,請以滑鼠右鍵按兩下 BLL 子資料夾,選擇 [新增專案],然後選擇 [類別] 範本。 將四個ProductsBLL類別命名為、 CategoriesBLLSuppliersBLLEmployeesBLL

將四個新類別新增至 App_Code資料夾

圖 2:將四個新類別新增至 App_Code 資料夾

接下來,讓我們將方法新增至每個類別,以簡單地封裝第一個教學課程中為 TableAdapters 定義的方法。 目前,這些方法只會直接呼叫 DAL;我們稍後會返回以新增任何必要的商業規則。

備註

如果您使用 Visual Studio Standard Edition 或更新版本(也就是您 不是 使用 Visual Web Developer),則可以選擇性地使用 類別設計工具以可視化方式設計類別。 如需 Visual Studio 中這項新功能的詳細資訊,請參閱 類別設計工具部落格

針對 類別 ProductsBLL ,我們需要新增總共七種方法:

  • GetProducts() 會傳回所有產品
  • GetProductByProductID(productID) 傳回具有指定之產品標識碼的產品
  • GetProductsByCategoryID(categoryID) 會傳回指定類別中的所有產品
  • GetProductsBySupplier(supplierID) 會傳回指定供應商的所有產品
  • AddProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued) 使用傳入的值,將新產品插入資料庫中;傳 ProductID 回新插入記錄的值
  • UpdateProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued, productID) 使用所傳入的值來更新資料庫中的現有產品;如果只有一個資料列被更新,則傳回 True,否則傳回 False
  • DeleteProduct(productID) 從資料庫刪除指定的產品

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

只要傳回數據 GetProductsGetProductByProductIDGetProductsByCategoryIDGetProductBySuppliersID 的方法就相當簡單,因為它們只會呼叫 DAL。 在某些情況下,可能需要在此層級實作商務規則(例如根據目前登入的使用者或使用者所屬角色的授權規則),我們只會將這些方法保留 as-is。 針對這些方法,BLL 僅僅作為演示層從資料存取層存取基礎數據的代理。

AddProductUpdateProduct 方法都會以參數的形式接受各種產品欄位的值,並分別新增新產品或更新現有的產品。 由於許多Product資料表的欄可以接受NULL值(例如 CategoryIDSupplierIDUnitPrice),因此對映至這些欄的AddProductUpdateProduct輸入參數會使用可空型別。 在 .NET 2.0 中引入的可空類型是一項新特性,提供方法用來表示數值型別是否應為 Nothing。 如需詳細資訊,請參閱 Paul Vick 的部落格文章 <關於可為 Null 的類型和 VB 的真相>, 以及 可為 Null 結構的技術檔。

這三種方法都會傳回 Boolean 值,指出是否插入、更新或刪除數據列,因為作業可能不會造成受影響的數據列。 例如,如果頁面開發人員針對不存在的產品呼叫 DeleteProduct 傳入 ProductID,那麼對資料庫發出的語句將不會產生任何作用,因此 DELETE 方法會傳回 DeleteProduct

請注意,新增新產品或更新現有產品時,我們會以新的或修改的產品域值作為純量清單,而不是接受 ProductsRow 實例。 之所以選擇這個方法,是因為 類別 ProductsRow 衍生自 ADO.NET DataRow 類別,該類別沒有預設無參數建構函式。 若要建立新的 ProductsRow 實例,我們必須先建立 ProductsDataTable 實例,然後叫用其 NewProductRow() 方法(我們在 中 AddProduct執行此動作)。 當我們轉向使用 ObjectDataSource 插入和更新產品時,這個缺點會顯現出來。 簡言之,ObjectDataSource 會嘗試建立輸入參數的實例。 如果 BLL 方法需要 ProductsRow 實例,ObjectDataSource 會嘗試建立實例,但因為缺少預設無參數建構函式而失敗。 如需此問題的詳細資訊,請參閱下列兩個 ASP.NET 論壇文章: 使用 Strongly-Typed DataSet 更新 ObjectDataSources,以及 使用 ObjectDataSource 和 Strongly-Typed DataSet 的問題

接下來,在 AddProductUpdateProduct中,程式代碼會 ProductsRow 建立 實例,並填入剛傳入的值。 當將值指派給 DataRow 的 DataColumns 時,可能會進行各種欄位層級驗證檢查。 因此,手動將傳入的值放回 DataRow,有助於確保將數據傳遞至 BLL 方法的有效性。 不幸的是,Visual Studio 所生成的強型別 DataRow 類別並未使用可空類型。 相反地,為了使 DataRow 中的特定 DataColumn 對應至 NULL 的資料庫值,我們必須使用 SetColumnNameNull() 方法。

UpdateProduct中,我們先載入需要更新的產品,然後使用GetProductByProductID(productID)進行更新。 雖然這似乎是多餘的資料庫操作,但在未來探討樂觀並發控制的教學課程中,這次額外操作將會證明其價值。 樂觀並發控制是一種技術,可確保同時對相同數據進行工作的兩位使用者不會不小心覆寫彼此的變更。 擷取整個記錄也可讓您更輕鬆地在 BLL 中建立只修改 DataRow 資料行子集的更新方法。 當我們探索 類別 SuppliersBLL 時,我們將會看到這類範例。

最後,請注意,於檔案頂端附近類別語句前的 ProductsBLL 語法中,類別 已套用 [System.ComponentModel.DataObject],而且方法具有 DataObjectMethodAttribute 屬性。 屬性 DataObject 會將 類別標示為適合系結至 ObjectDataSource 控件的物件,而 表示 DataObjectMethodAttribute 方法的用途。 如未來教學課程中所見,ASP.NET 2.0 的 ObjectDataSource 可讓您輕鬆地從類別宣告式存取數據。 為了協助篩選在 ObjectDataSource 精靈中系結至的可能類別清單,預設只會在精靈的下拉式清單中顯示標示為 DataObjects 的類別。 類別 ProductsBLL 在不使用這些屬性的情況下也能運作,但新增它們可讓您更輕鬆地在 ObjectDataSource 的精靈中使用。

新增其他類別

完成 ProductsBLL 類別之後,我們仍然需要新增類別,以便與類別、供應商和員工合作。 請花點時間使用上述範例中的概念來建立下列類別和方法:

  • CategoriesBLL.cs

    • GetCategories()
    • GetCategoryByCategoryID(categoryID)
  • SuppliersBLL.cs

    • GetSuppliers()
    • GetSupplierBySupplierID(supplierID)
    • GetSuppliersByCountry(country)
    • UpdateSupplierAddress(supplierID, address, city, country)
  • EmployeesBLL.cs

    • GetEmployees()
    • GetEmployeeByEmployeeID(employeeID)
    • GetEmployeesByManager(managerID)

值得指出 SuppliersBLL 的其中一個方法是 類別的 UpdateSupplierAddress 方法。 這個方法會提供介面,只更新供應商的地址資料。 在內部,這個方法會讀取SupplierDataRow物件,該物件是使用supplierID在指定的GetSupplierBySupplierID中獲得的,並設定其位址相關屬性,然後呼叫SupplierDataTableUpdate方法。 方法 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

如需 BLL 類別的完整實作,請參閱這篇文章的下載。

步驟 2:透過 BLL 類別存取具類型的數據集

在第一個教學課程中,我們看到以程序設計方式直接使用具型別數據集的範例,但是隨著 BLL 類別的新增,表示層應該改為針對 BLL 運作。 在第一個教學課程的 AllProducts.aspx 範例中, ProductsTableAdapter 是用來將產品清單系結至 GridView,如下列程式代碼所示:

Dim productsAdapter As New ProductsTableAdapter()
GridView1.DataSource = productsAdapter.GetProducts()
GridView1.DataBind()

若要使用新的 BLL 類別,所有需要變更的程式代碼都是第一行程式代碼,只需將 ProductsTableAdapter 物件取代為 ProductBLL 物件:

Dim productLogic As New ProductsBLL()
GridView1.DataSource = productLogic.GetProducts()
GridView1.DataBind()

BLL 類別以及具型別的數據集都可以透過使用 ObjectDataSource 以宣告方式存取。 我們將在下列教學課程中更詳細地討論 ObjectDataSource。

產品清單會顯示在 GridView 中

圖 3:產品清單會顯示在 GridView 中(按兩下以檢視完整大小的影像

步驟 3:將 Field-Level 驗證新增至 DataRow 類別

欄位層級驗證會在插入或更新時,檢查與商務對象的屬性值有關。 產品的某些欄位層級驗證規則包括:

  • 欄位 ProductName 長度必須少於 40 個字元
  • 欄位 QuantityPerUnit 長度必須少於 20 個字元
  • ProductIDProductNameDiscontinued欄位是必填的,但所有其他欄位都是選填的。
  • UnitPriceUnitsInStockUnitsOnOrderReorderLevel 欄位必須大於或等於零

這些規則可以且應該在資料庫層級表示。 在ProductName資料表中,QuantityPerUnitProducts欄位的字元限制由這些資料行的資料類型來擷取,分別為nvarchar(40)nvarchar(20)。 在資料庫表中,字段是必填還是可選取決於數據列是否允許NULL。 存在四個檢查約束,確保只有大於或等於零的值才能進入UnitPriceUnitsInStockUnitsOnOrderReorderLevel列。

除了在資料庫中強制執行這些規則之外,也應該在 DataSet 層級強制執行這些規則。 事實上,每個 DataTable 的 DataColumns 集合中都已經記錄了欄位長度,以及值是必需還是可選的。 若要查看自動提供的現有欄位層級驗證,請移至 [資料集設計工具],從其中一個 DataTable 選取字段,然後移至 [屬性] 視窗。 如圖 4 所示, QuantityPerUnit 中的 ProductsDataTable DataColumn 長度上限為 20 個字元,而且允許 NULL 值。 如果我們嘗試將 ProductsDataRowQuantityPerUnit 屬性設定為長度超過 20 個字元的字串值,則會拋出 ArgumentException

DataColumn 提供基本的 Field-Level 驗證

圖4: DataColumn 提供基本 Field-Level 驗證(點擊以查看全尺寸影像

不幸的是,我們無法透過 [屬性] 視窗指定界限檢查,例如 UnitPrice 值必須大於或等於零。 為了提供這種類型的欄位層級驗證,我們需要為 DataTable 的 ColumnChanging 事件建立事件處理程式。 如上一個 教學課程所述,Typed DataSet 所建立的 DataSet、DataTables 和 DataRow 物件可以透過使用部分類別來擴充。 使用這項技術,我們可以建立 ColumnChanging 類別的 ProductsDataTable 事件處理程式。 首先,在名為App_CodeProductsDataTable.ColumnChanging.vb資料夾中建立類別。

將新類別新增至 App_Code資料夾

圖 5:將新類別新增至 App_Code 資料夾 (按一下以檢視完整大小的影像

接下來,為 ColumnChanging 事件建立事件處理程式,以確保 UnitPriceUnitsInStockUnitsOnOrder、 和 ReorderLevel 資料行值 (如果不是 NULL) 大於或等於零。 如果有任何這類資料行超出範圍,則拋出 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

步驟 4:將自定義商務規則新增至 BLL 的類別

除了欄位層級驗證之外,可能還有一些涉及不同實體或概念的高階自定義商務規則,無法在單一數據行層級表示,例如:

  • 如果產品已停產,則無法更新UnitPrice
  • 員工的居住國家/地區必須與其經理的居住國相同
  • 如果產品是唯一由供應商提供的產品,則無法停止產品

BLL 類別應該包含檢查,以確保遵守應用程式的商務規則。 這些檢查可以直接新增至它們所應用的方法上。

假設我們的商務規則規定,如果產品是唯一來自指定供應商的產品,則無法標示為已中止。 也就是說,如果 product X 是唯一從供應商 Y 購買的產品,我們無法將 X 標示為已中止:不過,如果供應商 Y 為我們提供三個產品 ABC,我們可以將任何和所有產品標示為已停止。 奇怪的商務規則,但商務規則和常識不一定一致!

UpdateProducts 方法中強制執行此商務規則時,我們會先檢查Discontinued是否已被設定為True,如果是,我們會呼叫GetProductsBySupplierID來判斷我們從此產品的供應商處購買了多少產品。 如果只從此供應商購買一個產品,我們將作出一項操作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

回應表示層中的驗證錯誤

從表示層呼叫 BLL 時,我們可以決定是否嘗試處理任何可能引發的例外狀況,或讓它們向上冒泡到 ASP.NET(這會引發 HttpApplicationError 事件)。 若要以程式設計方式處理 BLL 時發生例外狀況,我們可以使用 Try...Catch 區塊,如下列範例所示:

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

如我們在未來的教學課程中所見,當使用資料 Web 控制項插入、更新或刪除資料時,可以直接在事件處理程式中處理從 BLL 擡升的例外狀況,而不需要將程式碼包裝在 Try...Catch 區塊中。

總結

架構良好的應用程式會設計成不同的層,每個層都會封裝特定角色。 在本文章系列的第一個教學課程中,我們使用具類型的數據集建立數據存取層;在本教學課程中,我們建置商業規則層作為應用程式 App_Code 資料夾中的一系列類別,可呼叫我們的 DAL。 BLL 會為應用程式實作欄位層級和業務層級邏輯。 除了建立個別的 BLL 之外,如本教學課程所示,另一個選項是使用部分類別來擴充 TableAdapters 的方法。 不過,使用這項技術不僅不允許我們修改現有的方法,也無法像本文中採用的方法那樣清晰地分隔 DAL 和 BLL。

完成 DAL 和 BLL 之後,我們就準備好開始進行演示層。 在下一個教學課程中,我們將從數據存取主題進行簡短的繞道,並定義一致的頁面配置,以在整個教學課程中使用。

快樂的程序設計!

關於作者

斯科特·米切爾,七本 ASP/ASP.NET 書籍和 4GuysFromRolla.com 創始人的作者,自1998年以來一直與Microsoft Web 技術合作。 斯科特擔任獨立顧問、教練和作家。 他的最新書是 Sams Teach Yourself ASP.NET 2.0 in 24 Hours。 可以透過 mitchell@4GuysFromRolla.com 聯絡他。

特別感謝

本教學系列已由許多熱心的評論者審閱。 本教學課程的主要檢閱者是 Liz Shulok、Dennis Patterson、Carlos Santos 和希爾頓 Giesenow。 有興趣檢閱我即將推出的 MSDN 文章嗎? 如果是,請在 mitchell@4GuysFromRolla.com給我留言。