Sdílet prostřednictvím


Vytvoření vrstvy obchodní logiky (VB)

od Scotta Mitchella

Stáhnout PDF

V tomto kurzu se dozvíte, jak centralizovat obchodní pravidla do vrstvy obchodní logiky (BLL), která slouží jako zprostředkovatel pro výměnu dat mezi prezentační vrstvou a DAL.

Úvod

Vrstva přístupu k datům (DAL) vytvořená v prvním kurzu čistě odděluje logiku přístupu k datům od logiky prezentace. Ačkoli DAL čistě odděluje podrobnosti o přístupu k datům od prezentační vrstvy, nevynucuje žádná obchodní pravidla, která se mohou vztahovat. Pro naši aplikaci můžeme například chtít zakázat úpravu polí CategoryID nebo SupplierID v tabulce Products v případě, že pole Discontinued je nastaveno na hodnotu 1, nebo můžeme chtít vynutit pravidla seniority, zakazující situaci, kdy je zaměstnanec spravován někým, kdo byl přijat později. Dalším běžným scénářem je autorizace, například pouze uživatelé v určité roli můžou odstranit produkty nebo změnit UnitPrice hodnotu.

V tomto kurzu se dozvíte, jak tato obchodní pravidla centralizovat do vrstvy obchodní logiky (BLL), která slouží jako zprostředkovatel pro výměnu dat mezi prezentační vrstvou a DAL. V reálné aplikaci by se BLL mělo implementovat jako samostatný projekt knihovny tříd; Pro tyto kurzy však implementujeme BLL jako řadu tříd v naší App_Code složce, abychom zjednodušili strukturu projektu. Obrázek 1 znázorňuje vztahy architektury mezi prezentační vrstvou, BLL a DAL.

BLL odděluje prezentační vrstvu od vrstvy přístupu k datům a ukládá obchodní pravidla.

Obrázek 1: BLL odděluje prezentační vrstvu od vrstvy přístupu k datům a ukládá obchodní pravidla.

Místo vytváření samostatných tříd pro implementaci obchodní logiky bychom mohli tuto logiku umístit přímo do typové datové sady s částečnými třídami. Příklad vytvoření a rozšíření typové datové sady najdete v prvním kurzu.

Krok 1: Vytvoření tříd BLL

Naše BLL bude složena ze čtyř tříd, jedna pro každý TableAdapter v DAL; každá z těchto tříd BLL bude obsahovat metody pro načítání, vkládání, aktualizaci a odstraňování z příslušného TableAdapteru v DAL, s aplikováním příslušných obchodních pravidel.

Pro oddělení tříd souvisejících s DAL a BLL vytvoříme ve složce App_Code dvě podsložky, DAL a BLL. Jednoduše klikněte pravým tlačítkem myši na App_Code složku v Průzkumníku řešení a zvolte Nová složka. Po vytvoření těchto dvou složek přesuňte Typed DataSet vytvořený v prvním kurzu do DAL podsložky.

Dále vytvořte čtyři soubory třídy BLL v BLL podsložce. Provedete to tak, že kliknete pravým tlačítkem na BLL podsložku, zvolíte Přidat novou položku a zvolíte šablonu Třídy. Pojmenujte čtyři třídy ProductsBLL, CategoriesBLL, SuppliersBLLa EmployeesBLL.

Přidání čtyř nových tříd do složky App_Code

Obrázek 2: Přidání čtyř nových tříd do App_Code složky

V dalším kroku přidáme metody do každé třídy, které jednoduše zabalí metody definované pro TableAdaptery z prvního kurzu. Prozatím tyto metody budou jen volat přímo do DAL; Později se vrátíme, abychom přidali jakoukoli potřebnou obchodní logiku.

Poznámka:

Pokud používáte Visual Studio Standard Edition nebo vyšší (to znamená, že nepoužíváte Visual Web Developer), můžete volitelně navrhnout třídy vizuálně pomocí Návrháře tříd. Další informace o této nové funkci v sadě Visual Studio najdete v blogu Návrháře tříd .

ProductsBLL Pro třídu potřebujeme přidat celkem sedm metod:

  • GetProducts() vrátí všechny produkty.
  • GetProductByProductID(productID) vrátí produkt se zadaným ID produktu.
  • GetProductsByCategoryID(categoryID) vrátí všechny produkty ze zadané kategorie.
  • GetProductsBySupplier(supplierID) vrátí všechny produkty od zadaného dodavatele.
  • AddProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued) vloží nový produkt do databáze pomocí předaných hodnot; ProductID vrátí hodnotu nově vloženého záznamu.
  • UpdateProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued, productID) aktualizuje existující produkt v databázi pomocí předaných hodnot; vrátí True , pokud byl právě jeden řádek aktualizován, False jinak
  • DeleteProduct(productID) odstraní zadaný produkt z databáze.

ProductsBLL.vb

Imports NorthwindTableAdapters

<System.ComponentModel.DataObject()> _
Public Class ProductsBLL

    Private _productsAdapter As ProductsTableAdapter = Nothing
    Protected ReadOnly Property Adapter() As ProductsTableAdapter
        Get
            If _productsAdapter Is Nothing Then
                _productsAdapter = New ProductsTableAdapter()
            End If

            Return _productsAdapter
        End Get
    End Property

    <System.ComponentModel.DataObjectMethodAttribute _
        (System.ComponentModel.DataObjectMethodType.Select, True)> _
    Public Function GetProducts() As Northwind.ProductsDataTable
        Return Adapter.GetProducts()
    End Function

    <System.ComponentModel.DataObjectMethodAttribute _
        (System.ComponentModel.DataObjectMethodType.Select, False)> _
    Public Function GetProductByProductID(ByVal productID As Integer) _
        As Northwind.ProductsDataTable
        Return Adapter.GetProductByProductID(productID)
    End Function

    <System.ComponentModel.DataObjectMethodAttribute _
        (System.ComponentModel.DataObjectMethodType.Select, False)> _
    Public Function GetProductsByCategoryID(ByVal categoryID As Integer) _
        As Northwind.ProductsDataTable
        Return Adapter.GetProductsByCategoryID(categoryID)
    End Function

    <System.ComponentModel.DataObjectMethodAttribute _
        (System.ComponentModel.DataObjectMethodType.Select, False)> _
    Public Function GetProductsBySupplierID(ByVal supplierID As Integer) _
        As Northwind.ProductsDataTable
        Return Adapter.GetProductsBySupplierID(supplierID)
    End Function

    <System.ComponentModel.DataObjectMethodAttribute _
        (System.ComponentModel.DataObjectMethodType.Insert, True)> _
    Public Function AddProduct( _
        productName As String, supplierID As Nullable(Of Integer), _
        categoryID As Nullable(Of Integer), quantityPerUnit As String, _
        unitPrice As Nullable(Of Decimal), unitsInStock As Nullable(Of Short), _
        unitsOnOrder As Nullable(Of Short), reorderLevel As Nullable(Of Short), _
        discontinued As Boolean) _
        As Boolean

        Dim products As New Northwind.ProductsDataTable()
        Dim product As Northwind.ProductsRow = products.NewProductsRow()

        product.ProductName = productName
        If Not supplierID.HasValue Then
            product.SetSupplierIDNull()
        Else
            product.SupplierID = supplierID.Value
        End If

        If Not categoryID.HasValue Then
            product.SetCategoryIDNull()
        Else
            product.CategoryID = categoryID.Value
        End If

        If quantityPerUnit Is Nothing Then
            product.SetQuantityPerUnitNull()
        Else
            product.QuantityPerUnit = quantityPerUnit
        End If

        If Not unitPrice.HasValue Then
            product.SetUnitPriceNull()
        Else
            product.UnitPrice = unitPrice.Value
        End If

        If Not unitsInStock.HasValue Then
            product.SetUnitsInStockNull()
        Else
            product.UnitsInStock = unitsInStock.Value
        End If

        If Not unitsOnOrder.HasValue Then
            product.SetUnitsOnOrderNull()
        Else
            product.UnitsOnOrder = unitsOnOrder.Value
        End If

        If Not reorderLevel.HasValue Then
            product.SetReorderLevelNull()
        Else
            product.ReorderLevel = reorderLevel.Value
        End If

        product.Discontinued = discontinued

        products.AddProductsRow(product)
        Dim rowsAffected As Integer = Adapter.Update(products)

        Return rowsAffected = 1
    End Function

    <System.ComponentModel.DataObjectMethodAttribute _
        (System.ComponentModel.DataObjectMethodType.Update, True)> _
    Public Function UpdateProduct(_
        productName As String, supplierID As Nullable(Of Integer), _
        categoryID As Nullable(Of Integer), quantityPerUnit As String, _
        unitPrice As Nullable(Of Decimal), unitsInStock As Nullable(Of Short), _
        unitsOnOrder As Nullable(Of Short), reorderLevel As Nullable(Of Short), _
        discontinued As Boolean, productID As Integer) _
        As Boolean

        Dim products As Northwind.ProductsDataTable = _
            Adapter.GetProductByProductID(productID)

        If products.Count = 0 Then
            Return False
        End If

        Dim product as Northwind.ProductsRow = products(0)

        product.ProductName = productName
        If Not supplierID.HasValue Then
            product.SetSupplierIDNull()
        Else
            product.SupplierID = supplierID.Value
        End If

        If Not categoryID.HasValue Then
            product.SetCategoryIDNull()
        Else
            product.CategoryID = categoryID.Value
        End If

        If quantityPerUnit Is Nothing Then
            product.SetQuantityPerUnitNull()
        Else
            product.QuantityPerUnit = quantityPerUnit
        End If

        If Not unitPrice.HasValue Then
            product.SetUnitPriceNull()
        Else
            product.UnitPrice = unitPrice.Value
        End If

        If Not unitsInStock.HasValue Then
            product.SetUnitsInStockNull()
        Else
            product.UnitsInStock = unitsInStock.Value
        End If

        If Not unitsOnOrder.HasValue Then
            product.SetUnitsOnOrderNull()
        Else
            product.UnitsOnOrder = unitsOnOrder.Value
        End If

        If Not reorderLevel.HasValue Then
            product.SetReorderLevelNull()
        Else
            product.ReorderLevel = reorderLevel.Value
        End If

        product.Discontinued = discontinued

        Dim rowsAffected As Integer = Adapter.Update(product)

        Return rowsAffected = 1
    End Function

    <System.ComponentModel.DataObjectMethodAttribute _
        (System.ComponentModel.DataObjectMethodType.Delete, True)> _
    Public Function DeleteProduct(ByVal productID As Integer) As Boolean
        Dim rowsAffected As Integer = Adapter.Delete(productID)

        Return rowsAffected = 1
    End Function
End Class

Metody, které jednoduše vracejí data GetProducts, GetProductByProductIDGetProductsByCategoryIDa GetProductBySuppliersID jsou poměrně jednoduché, protože jednoduše volají do DAL. V některých scénářích mohou existovat obchodní pravidla, která je potřeba implementovat na této úrovni (například autorizační pravidla založená na aktuálně přihlášeného uživatele nebo roli, do které uživatel patří), tyto metody jednoduše ponecháme as-is. Pro tyto metody pak BLL slouží pouze jako proxy, prostřednictvím kterého prezentační vrstva přistupuje k podkladovým datům z vrstvy přístupu k datům.

Obě metody AddProduct a UpdateProduct přebírají jako parametry hodnoty pro různá pole produktů a buď přidají nový produkt, nebo aktualizují existující. Vzhledem k tomu, že mnoho sloupců tabulky Product může přijímat hodnoty NULL (CategoryID, SupplierID a UnitPrice), abyste jmenovali několik, tyto vstupní parametry pro AddProduct a UpdateProduct, které se mapují na tyto sloupce, používají typy s možností null hodnot. Typy s možnou hodnotou null jsou nové pro .NET 2.0 a umožňují označit, zda má být typ hodnoty Nothing. Další informace najdete v blogovém příspěvku Paula Vicka nazvaném Pravda o typech Nullable a VB a v technické dokumentaci ke struktuře Nullable.

Všechny tři metody vrátí logickou hodnotu označující, zda byl řádek vložen, aktualizován nebo odstraněn, protože operace nemusí mít za následek ovlivněný řádek. Pokud například vývojář stránky volá DeleteProduct předávající neexistující ProductID jako argument, DELETE příkaz vydaný do databáze nebude mít žádný vliv, a proto metoda DeleteProduct vrátí False.

Všimněte si, že když přidáte nový produkt nebo aktualizujeme existující produkt, vezmeme hodnoty polí nového nebo upraveného produktu jako seznam skalárů na rozdíl od přijetí ProductsRow instance. Tento přístup byl zvolen, protože ProductsRow třída je odvozena od třídy ADO.NET DataRow , která nemá výchozí konstruktor bez parametrů. Abychom mohli vytvořit novou ProductsRow instanci, musíme nejprve vytvořit ProductsDataTable instanci a pak vyvolat její NewProductRow() metodu (kterou děláme v AddProduct). Když se obrátíme na vkládání a aktualizaci produktů pomocí ObjectDataSource, tento nedostatek se projevuje. Stručně řečeno, ObjectDataSource se pokusí vytvořit instanci vstupních parametrů. Pokud metoda BLL očekává ProductsRow instanci, ObjectDataSource se pokusí vytvořit jednu, ale selže kvůli nedostatku výchozího konstruktoru bez parametrů. Další informace o tomto problému naleznete v následujících dvou ASP.NET fór příspěvky: Aktualizace ObjectDataSources pomocí Strongly-Typed DataSets a Problém s ObjectDataSource a Strongly-Typed DataSet.

Dále v obou AddProduct a UpdateProduct kód vytvoří instanci ProductsRow a naplní ji hodnotami, které byly právě předány. Při přiřazování hodnot do DataColumnů DataRowu může dojít ke různým kontrolám ověření na úrovni polí. Proto ruční vložení předaných hodnot zpět do DataRow pomáhá zajistit platnost dat předávaných metodě BLL. Bohužel, silně typizované třídy DataRow vygenerované sadou Visual Studio nepoužívají nulovatelné typy. Chcete-li označit, že konkrétní DataColumn v DataRow by měl odpovídat hodnotě databáze NULL, musíme použít metodu SetColumnNameNull().

Nejprve UpdateProduct načteme produkt, který se má aktualizovat pomocí GetProductByProductID(productID). I když to může vypadat jako nepotřebná cesta do databáze, ukáže se tato další cesta jako užitečná v budoucích kurzech, které prozkoumají optimistickou souběžnost. Optimistická konkurenční technika zajistí, aby dva uživatelé, kteří současně pracují na stejných datech, náhodou nezaměnili změny toho druhého. Získání celého záznamu také usnadňuje vytváření aktualizačních metod v BLL, které upravují pouze podmnožinu sloupců DataRow. Když prozkoumáme SuppliersBLL třídu, uvidíme takový příklad.

Nakonec mějte na ProductsBLL paměti, že třída má použitý atribut DataObject (syntax [System.ComponentModel.DataObject] přímo před příkazem třídy v horní části souboru) a metody mají atributy DataObjectMethodAttribute. Atribut DataObject označuje třídu jako objekt vhodný pro vazbu na ObjectDataSource ovládacího prvku, zatímco DataObjectMethodAttribute označuje účel metody. Jak uvidíme v budoucích kurzech, ASP.NET ObjectDataSource 2.0 usnadňuje deklarativní přístup k datům z třídy. Chcete-li pomoci filtrovat seznam možných tříd, které se mají svázat v průvodci ObjectDataSource, ve výchozím nastavení jsou v rozevíracím seznamu průvodce zobrazeny pouze ty třídy, které jsou označeny jako DataObjects. Třída ProductsBLL bude fungovat i bez těchto atributů, ale jejich přidání usnadňuje práci v průvodci ObjectDataSource.

Přidání dalších tříd

S dokončenou ProductsBLL třídou stále potřebujeme přidat třídy pro práci s kategoriemi, dodavateli a zaměstnanci. Chvíli si vytvořte následující třídy a metody s využitím konceptů z výše uvedeného příkladu:

  • CategoriesBLL.cs

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

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

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

Za zmínku stojí metoda SuppliersBLL třídy UpdateSupplierAddress . Tato metoda poskytuje rozhraní pro aktualizaci pouze informací o adrese dodavatele. Interně tato metoda čte objekt SupplierDataRow pro zadané supplierID (používá se GetSupplierBySupplierID), nastaví jeho vlastnosti související s adresou a pak volá ke SupplierDataTable metodě objektu Update. Metoda UpdateSupplierAddress následuje:

<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

V tomto článku si můžete stáhnout kompletní implementaci tříd BLL.

Krok 2: Přístup k typed dataSets prostřednictvím tříd BLL

V prvním kurzu jsme viděli příklady práce přímo s typed DataSet programově, ale s přidáním našich tříd BLL by prezentační vrstva měla místo toho pracovat s BLL. V příkladu AllProducts.aspx z prvního kurzu ProductsTableAdapter se použilo k vytvoření vazby seznamu produktů k Objektu GridView, jak je znázorněno v následujícím kódu:

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

Chcete-li použít nové třídy BLL, vše, co je třeba změnit, je první řádek kódu jednoduše nahradit ProductsTableAdapter objekt objektem ProductBLL :

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

Třídy BLL lze také přistupovat deklarativním způsobem (stejně jako Typed DataSet) pomocí ObjectDataSource. Podrobněji probereme ObjectDataSource v následujících kurzech.

Seznam produktů se zobrazí v objektu GridView.

Obrázek 3: Seznam produktů se zobrazí v objektu GridView (kliknutím zobrazíte obrázek s plnou velikostí).

Krok 3: Přidání ověřování Field-Level do tříd DataRow

Ověřování na úrovni pole jsou kontroly, které se týkají hodnot vlastností obchodních objektů při vkládání nebo aktualizaci. Mezi ověřovací pravidla na úrovni pole pro produkty patří:

  • Pole ProductName musí mít délku 40 znaků nebo méně.
  • Pole QuantityPerUnit musí mít délku 20 znaků nebo méně.
  • Pole ProductIDa ProductName pole Discontinuedjsou povinná, ale všechna ostatní pole jsou volitelná.
  • Pole UnitPrice, UnitsInStock, UnitsOnOrdera ReorderLevel musí být větší nebo rovna nule.

Tato pravidla můžou a měla by být vyjádřena na úrovni databáze. Omezení znaků pro pole ProductName a QuantityPerUnit pole jsou zachyceny datovými typy těchto sloupců v Products tabulce (nvarchar(40) a nvarchar(20)v uvedeném pořadí). Určuje, jestli jsou pole povinná a nepovinná, pokud sloupec tabulky databáze umožňuje NULL . Existují čtyři omezení kontroly , která zajišťují, že pouze hodnoty větší než nebo rovny nule můžou být v objektu UnitPrice, UnitsInStock, UnitsOnOrdernebo ReorderLevel ve sloupcích.

Kromě vynucování těchto pravidel v databázi by se měla vynucovat také na úrovni datové sady. Délka pole a to, jestli je hodnota povinná nebo nepovinná, jsou ve skutečnosti zaznamenány pro každou sadu DataColumns datové tabulky. Pokud chcete zobrazit existující ověřování na úrovni pole automaticky, přejděte do Návrháře datové sady, vyberte pole z jedné z datových tabulek a pak přejděte do okna Vlastnosti. Jak je znázorněno na obrázku 4, QuantityPerUnit DataColumn v ProductsDataTable má maximální délku 20 znaků a umožňuje NULL hodnoty. Pokud se pokusíme nastavit vlastnost ProductsDataRowQuantityPerUnit na řetězcovou hodnotu delší než 20 znaků, bude vyvolána chyba ArgumentException.

DataColumn poskytuje základní validaci Field-Level

Obrázek 4: Datový sloupec poskytuje základní ověřování Field-Level (kliknutím zobrazíte obrázek v plné velikosti).

Bohužel nemůžeme určit kontroly hranic, například UnitPrice hodnota musí být větší nebo rovna nule, prostřednictvím okna Vlastnosti. Abychom mohli poskytnout tento typ ověřování na úrovni pole, potřebujeme vytvořit obslužnou rutinu události pro událost ColumnChanging tabulky DataTable. Jak je uvedeno v předchozím kurzu, objekty DataSet, DataTables a DataRow vytvořené Typed DataSet lze rozšířit pomocí částečných tříd. Pomocí této techniky můžeme vytvořit obslužnou rutinu události ColumnChanging pro třídu ProductsDataTable. Začněte vytvořením třídy ve App_Code složce s názvem ProductsDataTable.ColumnChanging.vb.

Přidání nové třídy do složky App_Code

Obrázek 5: Přidání nové třídy do App_Code složky (kliknutím zobrazíte obrázek v plné velikosti)

Dále vytvořte obslužnou rutinu pro událost ColumnChanging, která zajišťuje, že hodnoty sloupců UnitPrice, UnitsInStock, UnitsOnOrder, a ReorderLevel (pokud ne NULL) jsou větší nebo rovny nule. V případě, že je některý takový sloupec mimo rozsah, vyvolejte výjimku ArgumentException.

ProductsDataTable.ColumnChanging.vb

Imports System.data

Partial Public Class Northwind
    Partial Public Class ProductsDataTable
        Public Overrides Sub BeginInit()
            AddHandler Me.ColumnChanging, AddressOf ValidateColumn
        End Sub

        Sub ValidateColumn(sender As Object, e As DataColumnChangeEventArgs)
            If e.Column.Equals(Me.UnitPriceColumn) Then
                If Not Convert.IsDBNull(e.ProposedValue) AndAlso _
                    CType(e.ProposedValue, Decimal) < 0 Then
                    Throw New ArgumentException( _
                        "UnitPrice cannot be less than zero", "UnitPrice")
                End If
            ElseIf e.Column.Equals(Me.UnitsInStockColumn) OrElse _
                e.Column.Equals(Me.UnitsOnOrderColumn) OrElse _
                e.Column.Equals(Me.ReorderLevelColumn) Then
                If Not Convert.IsDBNull(e.ProposedValue) AndAlso _
                    CType(e.ProposedValue, Short) < 0 Then
                    Throw New ArgumentException(String.Format( _
                        "{0} cannot be less than zero", e.Column.ColumnName), _
                        e.Column.ColumnName)
                End If
            End If
        End Sub
    End Class
End Class

Krok 4: Přidání vlastních obchodních pravidel do tříd podnikové logiky (BLL)

Kromě ověřování na úrovni polí můžou existovat vlastní obchodní pravidla vysoké úrovně, která zahrnují různé entity nebo koncepty, které se nedají vyjádřit na úrovni jednoho sloupce, například:

  • Pokud je produkt ukončený, nejde ho UnitPrice aktualizovat.
  • Země bydliště zaměstnance musí být stejná jako země bydliště svého manažera.
  • Produkt nelze ukončit, pokud je jediným produktem poskytovaným dodavatelem.

Třídy BLL by měly obsahovat kontroly, které zajistí dodržování obchodních pravidel aplikace. Tyto kontroly lze přidat přímo do metod, na které se vztahují.

Představte si, že naše obchodní pravidla určují, že produkt nemůže být označen jako ukončený, pokud je jediným produktem od daného dodavatele. To znamená, že pokud byl produkt X jediným produktem, který jsme koupili od dodavatele Y, nemohli jsme označit X jako ukončený; pokud nám ale dodavatel Y poskytl tři produkty, A, B a C, mohli bychom všechny tyto produkty označit jako ukončené. Liché obchodní pravidlo, ale obchodní pravidla a zdravý rozum nejsou vždy sladěné!

Abychom toto obchodní pravidlo vynutili v metodě UpdateProducts, začneme kontrolou, zda je Discontinued nastaveno na True, a pokud ano, zavoláme GetProductsBySupplierID, abychom zjistili, kolik produktů jsme zakoupili od dodavatele tohoto produktu. Pokud je od tohoto dodavatele zakoupen pouze jeden produkt, vyhodíme ApplicationException.

<System.ComponentModel.DataObjectMethodAttribute_
    (System.ComponentModel.DataObjectMethodType.Update, True)> _
Public Function UpdateProduct( _
    productName As String, supplierID As Nullable(Of Integer), _
    categoryID As Nullable(Of Integer), quantityPerUnit As String, _
    unitPrice As Nullable(Of Decimal), unitsInStock As Nullable(Of Short), _
    unitsOnOrder As Nullable(Of Short), reorderLevel As Nullable(Of Short), _
    discontinued As Boolean, productID As Integer) _
    As Boolean

    Dim products As Northwind.ProductsDataTable = _
        Adapter.GetProductByProductID(productID)

    If products.Count = 0 Then
        Return False
    End If

    Dim product As Northwind.ProductsRow = products(0)

    If discontinued Then
        Dim productsBySupplier As Northwind.ProductsDataTable = _
            Adapter.GetProductsBySupplierID(product.SupplierID)

        If productsBySupplier.Count = 1 Then
            Throw New ApplicationException( _
                "You cannot mark a product as discontinued if it is " & _
                "the only product purchased from a supplier")
        End If
    End If

    product.ProductName = productName

    If Not supplierID.HasValue Then
        product.SetSupplierIDNull()
    Else
        product.SupplierID = supplierID.Value
    End If

    If Not categoryID.HasValue Then
        product.SetCategoryIDNull()
    Else
        product.CategoryID = categoryID.Value
    End If

    If quantityPerUnit Is Nothing Then
        product.SetQuantityPerUnitNull()
    Else
        product.QuantityPerUnit = quantityPerUnit
    End If

    If Not unitPrice.HasValue Then
        product.SetUnitPriceNull()
    Else
        product.UnitPrice = unitPrice.Value
    End If

    If Not unitsInStock.HasValue Then
        product.SetUnitsInStockNull()
    Else
        product.UnitsInStock = unitsInStock.Value
    End If

    If Not unitsOnOrder.HasValue Then
        product.SetUnitsOnOrderNull()
    Else
        product.UnitsOnOrder = unitsOnOrder.Value
    End If

    If Not reorderLevel.HasValue Then
        product.SetReorderLevelNull()
    Else
        product.ReorderLevel = reorderLevel.Value
    End If

    product.Discontinued = discontinued

    Dim rowsAffected As Integer = Adapter.Update(product)

    Return rowsAffected = 1
End Function

Reakce na chyby ověřování v prezentační vrstvě

Při volání BLL z prezentační vrstvy můžeme rozhodnout, zda se pokusíme zpracovat jakékoli výjimky, které by mohly být vyvolány, nebo je necháme propagovat do ASP.NET (což vyvolá událost HttpApplicationError). K zpracování výjimky při práci s BLL programově můžeme použít Try...Catch blok, jak ukazuje následující příklad:

Dim productLogic As New ProductsBLL()

Try
    productLogic.UpdateProduct("Scotts Tea", 1, 1, Nothing, _
      -14, 10, Nothing, Nothing, False, 1)
Catch ae As ArgumentException
    Response.Write("There was a problem: " & ae.Message)
End Try

Jak uvidíme v budoucích kurzech, zpracování výjimek, které pocházejí z vrstvy BLL při použití datového webového ovladače pro vkládání, aktualizaci nebo mazání dat, je možné zpracovávat přímo v obslužné rutině události, takže není nutné kód obalovat do bloků Try...Catch.

Shrnutí

Dobře navržená aplikace je rozdělena do různých vrstev, z nichž každá zapouzdřuje určitou roli. V prvním kurzu této série článků jsme vytvořili vrstvu přístupu k datům pomocí typed DataSets; v tomto kurzu jsme vytvořili vrstvu obchodní logiky jako řadu tříd ve složce naší aplikace App_Code , která volá do naší DAL. BLL implementuje logiku na úrovni pole a obchodní úrovně pro naši aplikaci. Kromě vytvoření samostatného BLL, jak jsme to udělali v tomto kurzu, je další možností rozšířit metody TableAdapter pomocí částečné třídy. Použití této techniky nám však neumožňuje přepsat stávající metody, ani oddělit naše DAL a BLL tak čistě jako přístup, který jsme popsali v tomto článku.

Po dokončení DAL a BLL jsme připraveni začít na naší prezentační vrstvě. V dalším tutoriálu si uděláme krátkou odbočku od témat přístupu k datům a definujeme konzistentní rozložení stránky pro použití v rámci tutoriálů.

Šťastné programování!

O autorovi

Scott Mitchell, autor sedmi knih ASP/ASP.NET a zakladatel 4GuysFromRolla.com, pracuje s webovými technologiemi Microsoftu od roku 1998. Scott pracuje jako nezávislý konzultant, trenér a spisovatel. Jeho nejnovější kniha je Sams: Nauč se ASP.NET 2.0 za 24 hodin. Může být dosažitelný na mitchell@4GuysFromRolla.comadrese .

Zvláštní díky

Tato série kurzů byla zkontrolována mnoha užitečnými recenzenty. Vedoucí hodnotící tohoto kurzu byli Liz Shulok, Dennis Patterson, Carlos Santos a Hilton Giesenow. Chcete si projít nadcházející články MSDN? Pokud ano, napište mi zprávu na mitchell@4GuysFromRolla.com.