Sdílet prostřednictvím


Vytvoření vrstvy obchodní logiky (VB)

Scott Mitchell

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. I když dal čistě odděluje podrobnosti o přístupu k datům od prezentační vrstvy, nevynucuje žádná obchodní pravidla, která by se můžou použít. U naší aplikace můžeme například zakázat úpravu CategoryIDProducts polí nebo SupplierID tabulky, když Discontinued je pole nastaveno na hodnotu 1, nebo můžeme chtít vynutit pravidla seniority, která zakazují situace, kdy je zaměstnanec spravován někým, kdo ho najal. Dalším běžným scénářem je autorizace, která může odstranit produkty nebo změnit UnitPrice hodnotu pouze uživatelům v určité roli.

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 BLL měl být implementován 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šovali 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: Funkce 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 tuto logiku mohli případně 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 se bude skládat ze čtyř tříd, jedné pro každou TableAdapter v DAL; každá z těchto tříd BLL bude mít metody pro načtení, vložení, aktualizaci a odstranění z příslušného objektu TableAdapter v DAL, přičemž se použijí příslušná obchodní pravidla.

Pokud chcete přesněji oddělit třídy související s DAL a BLL, vytvoříme ve App_Code složce DAL dvě podsložky a BLL. Jednoduše klikněte pravým tlačítkem na App_Code složku v Průzkumník řešení a zvolte Nová složka. Po vytvoření těchto dvou složek přesuňte sadu Typed DataSet vytvořenou v prvním kurzu do podsložky DAL .

Dále v BLL podsložce vytvořte čtyři soubory tříd BLL. Chcete-li to provést, klikněte pravým tlačítkem na BLL podsložku, zvolte Přidat novou položku a zvolte šablonu Třída. Pojmenujte čtyři třídy ProductsBLL, SuppliersBLLCategoriesBLL, a 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

Dále do každé třídy přidáme metody, které jednoduše zabalí metody definované pro Objekty TableAdapter z prvního kurzu. Prozatím budou tyto metody pouze volat přímo do DAL; později se vrátíme a přidáme veškerou 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í třídy Designer. Další informace o této nové funkci v sadě Visual Studio najdete na blogu o třídě Designer.

ProductsBLL Pro třídu musíme 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, GetProductByProductID, GetProductsByCategoryIDa GetProductBySuppliersID jsou poměrně jednoduché, protože jednoduše volají do DAL. I když 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, ke které uživatel patří), tyto metody jednoduše ponecháme tak, jak jsou. Pro tyto metody pak BLL slouží pouze jako proxy server, jehož prostřednictvím prezentační vrstva přistupuje k podkladovým datům z vrstvy přístupu k datům.

Metody AddProduct a UpdateProduct přebírají jako parametry hodnoty pro různá pole produktů a přidávají nový produkt nebo aktualizují existující produkt. Vzhledem k tomu, SupplierIDže mnoho Product sloupců tabulky může přijímat NULL hodnoty (CategoryID, a UnitPrice, abychom jmenovali několik), tyto vstupní parametry pro AddProduct aUpdateProduct, které se mapují na tyto sloupce, používají typy s možnou hodnotou null. Typy s možnou hodnotou null jsou v rozhraní .NET 2.0 nové a poskytují techniku pro určení, jestli by měl být Nothinghodnotový typ místo toho . Další informace najdete v blogové položce Paula VickaPravda o typech s možnou hodnotou null a VB a v technické dokumentaci pro strukturu 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 zavolá předání pro ProductID neexistující produkt, DELETE příkaz vydaný do databáze nebude mít žádný vliv, a proto DeleteProduct metoda vrátí False.DeleteProduct

Všimněte si, že při přidávání nového produktu nebo aktualizaci existujícího produktu bereme hodnoty polí nového nebo upraveného produktu jako seznam skalárů, nikoli jako přijetí ProductsRow instance. Tento přístup byl zvolen, protože ProductsRow třída je odvozena od třídy ADO.NETDataRow, 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 (což děláme v AddProductnástroji ). Tento nedostatek se vymění za hlavu, když se obrátíme na vkládání a aktualizaci produktů pomocí ObjectDataSource. 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, ale selže kvůli nedostatku výchozího konstruktoru bez parametrů. Další informace o tomto problému najdete v následujících dvou příspěvcích ASP.NET fór: Aktualizace objectDataSources pomocí Strongly-Typed datových sad a Problém s objekty ObjectDataSource a Strongly-Typed DataSet.

Dále v nástroji i AddProductUpdateProductvytvoří ProductsRow kód instanci a naplní ji hodnotami, které jste právě předali. Při přiřazování hodnot do objektů DataColumns dataRow může dojít k různým ověřovacím kontrolám na úrovni pole. Proto ruční vložení předaných hodnot zpět do DataRow pomáhá zajistit platnost dat předávaných do metody BLL. Třídy DataRow se silnými typy vygenerované sadou Visual Studio bohužel nepoužívají typy s možnou hodnotou null. Místo toho, abychom označit, že konkrétní DataColumn v DataRow by měl odpovídat hodnotě NULL databáze, musíme použít metodu SetColumnNameNull() .

V UpdateProduct nástroji nejprve načteme produkt a aktualizujeme ho pomocí GetProductByProductID(productID). I když se to může zdát jako zbytečný výlet do databáze, tento dodatečný výlet se ukáže jako užitečný v budoucích kurzech, které zkoumají optimistickou souběžnost. Optimistická souběžnost je technika, která zajišťuje, aby se dva uživatelé, kteří současně pracují na stejných datech, omylem nepřepsali změny druhého. Získání celého záznamu také usnadňuje vytváření metod aktualizace v BLL, které upravují pouze podmnožinu sloupců DataRow. Když prozkoumáme SuppliersBLL třídu, uvidíme takový příklad.

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

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

ProductsBLL Po dokončení třídy musíme přidat třídy pro práci s kategoriemi, dodavateli a zaměstnanci. Využijte koncepty z výše uvedeného příkladu k vytvoření následujících tříd a metod:

  • CategoriesBLL.cs

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

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

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

Jedinou metodou, která stojí za zmínku, SuppliersBLL je metoda třídy UpdateSupplierAddress . Tato metoda poskytuje rozhraní pro aktualizaci pouze informací o adrese dodavatele. Interně tato metoda čte v objektu SupplierDataRow pro zadaný supplierID (pomocí GetSupplierBySupplierID), nastaví jeho vlastnosti související s adresou a pak zavolá do SupplierDataTablemetody 's Update . Metoda je UpdateSupplierAddress následující:

<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

Kompletní implementaci tříd BLL najdete ve stažení tohoto článku.

Krok 2: Přístup k zadaným datovým sadám 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 byl použit k vytvoření vazby seznamu produktů na 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, stačí změnit pouze první řádek kódu a jednoduše nahradit ProductsTableAdapter objekt objektem ProductBLL :

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

K třídám BLL lze také přistupovat deklarativně (stejně jako Typed DataSet) pomocí ObjectDataSource. ObjectDataSource si podrobněji probereme v následujících kurzech.

Seznam produktů se zobrazí v zobrazení GridView.

Obrázek 3: Seznam produktů se zobrazí v zobrazení GridView (kliknutím zobrazíte obrázek v plné velikosti)

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

Ověření 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 nejméně 40 znaků.
  • Pole QuantityPerUnit musí mít délku nejméně 20 znaků.
  • Pole ProductID, ProductNamea Discontinued jsou povinná, ale všechna ostatní pole jsou volitelná.
  • Pole UnitPrice, UnitsInStock, UnitsOnOrdera ReorderLevel musí být větší než nebo rovna nule.

Tato pravidla je možné a měla by být vyjádřena na úrovni databáze. Omezení ProductName počtu znaků v polích a QuantityPerUnit 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 volitelná, pokud sloupec tabulky databáze umožňuje NULL s. Existují čtyři omezení kontroly , která zajišťují, že do UnitPricesloupců , UnitsInStock, UnitsOnOrder, nebo ReorderLevel se můžou dostat pouze hodnoty větší nebo rovné nule.

Kromě vynucování těchto pravidel v databázi by se měla vynucovat také na úrovni datové sady. Ve skutečnosti se délka pole a to, jestli je hodnota povinná nebo volitelná, už zaznamenávají pro každou sadu DataTable DataColumns. Pokud chcete zobrazit existující ověření na úrovni pole automaticky, přejděte na Designer Datová sada, vyberte pole z některé z datových tabulek a pak přejděte na okno Vlastnosti. Jak ukazuje obrázek 4, QuantityPerUnit sloupec DataColumn v souboru ProductsDataTable má maximální délku 20 znaků a povoluje NULL hodnoty. Pokud se pokusíme nastavit ProductsDataRowQuantityPerUnit vlastnost na řetězcovou hodnotu delší než 20 znaků, ArgumentException vyvolá se to.

DataColumn poskytuje základní ověřování Field-Level.

Obrázek 4: DataColumn poskytuje základní Field-Level ověření (kliknutím zobrazíte obrázek v plné velikosti)

Bohužel prostřednictvím okno Vlastnosti nemůžeme určit kontroly hranic, například UnitPrice hodnota musí být větší než nebo rovna nule. 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 bylo zmíněno v předchozím kurzu, DataSet, DataTables a DataRow objekty vytvořené Typed DataSet lze rozšířit pomocí částečných tříd. Pomocí této techniky můžeme vytvořit obslužnou rutinu ColumnChangingProductsDataTable události pro třídu . 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 ColumnChanging události pro událost, která zajistí, aby UnitPricehodnoty sloupců , UnitsInStockUnitsOnOrder, a ReorderLevel (pokud neNULL) byly větší než nebo se rovna nule. Pokud je některý z takových sloupců mimo rozsah, vyvolá se 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 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 na úrovni jednoho sloupce nedají vyjádřit, například:

  • Pokud je produkt ukončen, nelze ho UnitPrice aktualizovat.
  • Země bydliště zaměstnance musí být stejná jako země bydliště nadřízenýho.
  • Produkt nelze ukončit, pokud se jedná o jediný produkt poskytnutý 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 nelze označit jako ukončený, pokud se jedná o jediný produkt od daného dodavatele. To znamená, že pokud byl produkt X jediným produktem, který jsme zakoupili od dodavatele Y, nemohli bychom označit X jako ukončený; Pokud nám však dodavatel Y dodal tři výrobky , A, B a C, pak bychom mohli označit všechny tyto produkty jako ukončené. Zvláštní obchodní pravidlo, ale obchodní pravidla a zdravý rozum nejsou vždy sladěné!

Pokud chceme toto obchodní pravidlo vynutit v UpdateProducts metodě, začneme tím, že zkontrolujeme, jestli Discontinued je nastavené na True hodnotu , a pokud ano, zavoláme GetProductsBySupplierID , abychom zjistili, kolik produktů jsme koupili 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 se můžeme rozhodnout, jestli se pokusíme zpracovat případné výjimky, které by mohly být vyvolány, nebo je nechat vybouchnout až do ASP.NET (což vyvolá HttpApplicationudálost ).Error Ke zpracování výjimky při práci s BLL prostřednictvím kódu programu můžeme použít try... Blok catch , 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é se vygenerují z BLL při použití webového datového ovládacího prvku pro vkládání, aktualizaci nebo odstraňování dat, může být zpracováno přímo v obslužné rutině události, na rozdíl od zalamování kódu do Try...Catch bloků.

Souhrn

Dobře navržená aplikace je sestavená do odlišný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é volají dolů do 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šíření metod TableAdapter pomocí částečných tříd. Použití této techniky nám však neumožňuje přepsat existující metody ani neodděluje dal a BLL tak čistě jako přístup, který jsme provedli v tomto článku.

Po dokončení DAL a BLL jsme připraveni začít s naší prezentační vrstvou. V dalším kurzu se krátce podíváme na témata o přístupu k datům a nadefinujeme konzistentní rozložení stránky, které se bude používat v rámci kurzů.

Všechno nejlepší na programování!

O autorovi

Scott Mitchell, autor sedmi knih o ASP/ASP.NET a zakladatel 4GuysFromRolla.com, pracuje s webovými technologiemi Microsoftu od roku 1998. Scott pracuje jako nezávislý konzultant, školitel a spisovatel. Jeho nejnovější kniha je Sams Teach Yourself ASP.NET 2.0 in 24 Hours. Můžete ho zastihnout na mitchell@4GuysFromRolla.comadrese . nebo prostřednictvím jeho blogu, který najdete na adrese http://ScottOnWriting.NET.

Zvláštní poděkování

Tato série kurzů byla zkontrolována mnoha užitečnými recenzenty. Hlavními recenzenty pro tento kurz byli Liz Shulok, Dennis Patterson, Carlos Santos a Hilton Giesenow. Chtěli byste si projít své nadcházející články na webu MSDN? Pokud ano, dejte mi řádek na mitchell@4GuysFromRolla.com.