Sdílet prostřednictvím


Vytvoření vrstvy obchodní logiky (C#)

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 DAL (Data Access Layer) vytvořená v prvním kurzu čistě odděluje logiku přístupu k datům od logiky prezentace. I když ale DAL čistě odděluje podrobnosti o přístupu k datům od prezentační vrstvy, nevynucuje žádná obchodní pravidla, která by se dala použít. Pro naši aplikaci můžeme například zakázat úpravu CategoryIDProducts polí nebo SupplierID v tabulce, 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 byl přijat po nich. Dalším běžným scénářem je autorizace, která může odstranit produkty nebo změnit UnitPrice hodnotu pouze uživatelů 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ěla být implementována 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 architektonické vztahy 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.

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

Naše BLL se bude skládat ze čtyř tříd, jednu pro každý 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.

Pro přehlednější oddělení tříd souvisejících 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 Typed DataSet vytvořenou v prvním kurzu do podsložky DAL .

Dále v BLL podsložce vytvořte čtyři soubory třídy BLL. To provedete tak, že kliknete pravým tlačítkem na BLL podsložku, zvolíte Přidat novou položku a zvolíte šablonu Třída. 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 do každé třídy metody, které jednoduše zabalí metody definované pro objekty TableAdapter z prvního kurzu. Prozatím bude 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 Designer třídy.

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í hodnotu true , pokud byl právě jeden řádek aktualizován, false jinak
  • DeleteProduct(productID) odstraní zadaný produkt z databáze.

ProductsBLL.cs

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using NorthwindTableAdapters;

[System.ComponentModel.DataObject]
public class ProductsBLL
{
    private ProductsTableAdapter _productsAdapter = null;
    protected ProductsTableAdapter Adapter
    {
        get {
            if (_productsAdapter == null)
                _productsAdapter = new ProductsTableAdapter();

            return _productsAdapter;
        }
    }

    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Select, true)]
    public Northwind.ProductsDataTable GetProducts()
    {
        return Adapter.GetProducts();
    }

    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Select, false)]
    public Northwind.ProductsDataTable GetProductByProductID(int productID)
    {
        return Adapter.GetProductByProductID(productID);
    }

    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Select, false)]
    public Northwind.ProductsDataTable GetProductsByCategoryID(int categoryID)
    {
        return Adapter.GetProductsByCategoryID(categoryID);
    }

    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Select, false)]
    public Northwind.ProductsDataTable GetProductsBySupplierID(int supplierID)
    {
        return Adapter.GetProductsBySupplierID(supplierID);
    }
    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Insert, true)]
    public bool AddProduct(string productName, int? supplierID, int? categoryID,
        string quantityPerUnit, decimal? unitPrice,  short? unitsInStock,
        short? unitsOnOrder, short? reorderLevel, bool discontinued)
    {
        // Create a new ProductRow instance
        Northwind.ProductsDataTable products = new Northwind.ProductsDataTable();
        Northwind.ProductsRow product = products.NewProductsRow();

        product.ProductName = productName;
        if (supplierID == null) product.SetSupplierIDNull();
          else product.SupplierID = supplierID.Value;
        if (categoryID == null) product.SetCategoryIDNull();
          else product.CategoryID = categoryID.Value;
        if (quantityPerUnit == null) product.SetQuantityPerUnitNull();
          else product.QuantityPerUnit = quantityPerUnit;
        if (unitPrice == null) product.SetUnitPriceNull();
          else product.UnitPrice = unitPrice.Value;
        if (unitsInStock == null) product.SetUnitsInStockNull();
          else product.UnitsInStock = unitsInStock.Value;
        if (unitsOnOrder == null) product.SetUnitsOnOrderNull();
          else product.UnitsOnOrder = unitsOnOrder.Value;
        if (reorderLevel == null) product.SetReorderLevelNull();
          else product.ReorderLevel = reorderLevel.Value;
        product.Discontinued = discontinued;

        // Add the new product
        products.AddProductsRow(product);
        int rowsAffected = Adapter.Update(products);

        // Return true if precisely one row was inserted,
        // otherwise false
        return rowsAffected == 1;
    }

    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Update, true)]
    public bool UpdateProduct(string productName, int? supplierID, int? categoryID,
        string quantityPerUnit, decimal? unitPrice, short? unitsInStock,
        short? unitsOnOrder, short? reorderLevel, bool discontinued, int productID)
    {
        Northwind.ProductsDataTable products = Adapter.GetProductByProductID(productID);
        if (products.Count == 0)
            // no matching record found, return false
            return false;

        Northwind.ProductsRow product = products[0];

        product.ProductName = productName;
        if (supplierID == null) product.SetSupplierIDNull();
          else product.SupplierID = supplierID.Value;
        if (categoryID == null) product.SetCategoryIDNull();
          else product.CategoryID = categoryID.Value;
        if (quantityPerUnit == null) product.SetQuantityPerUnitNull();
          else product.QuantityPerUnit = quantityPerUnit;
        if (unitPrice == null) product.SetUnitPriceNull();
          else product.UnitPrice = unitPrice.Value;
        if (unitsInStock == null) product.SetUnitsInStockNull();
          else product.UnitsInStock = unitsInStock.Value;
        if (unitsOnOrder == null) product.SetUnitsOnOrderNull();
          else product.UnitsOnOrder = unitsOnOrder.Value;
        if (reorderLevel == null) product.SetReorderLevelNull();
          else product.ReorderLevel = reorderLevel.Value;
        product.Discontinued = discontinued;

        // Update the product record
        int rowsAffected = Adapter.Update(product);

        // Return true if precisely one row was updated,
        // otherwise false
        return rowsAffected == 1;
    }

    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Delete, true)]
    public bool DeleteProduct(int productID)
    {
        int rowsAffected = Adapter.Delete(productID);

        // Return true if precisely one row was deleted,
        // otherwise false
        return rowsAffected == 1;
    }
}

Metody, které jednoduše vrací 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 můžou 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 beze stavu. Pro tyto metody pak BLL slouží pouze jako proxy, přes který 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řidají nový produkt nebo aktualizují existující. Vzhledem k tomu, že mnoho Product sloupců tabulky může přijímat NULL hodnoty (CategoryID, SupplierIDa , abychom UnitPricejmenovali jen některé), tyto vstupní parametry pro AddProduct a UpdateProduct , 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 nullhodnotový typ místo toho . V jazyce C# můžete označit typ hodnoty jako typ s možnou hodnotou null přidáním ? za typ (například int? x;). Další informace najdete v části Typy s možnou hodnotou null v Průvodci programováním v C# .

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 vystavený databázi 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 ProductsRow přijetí 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 AddProduct). Tento nedostatek se vymění za hlavu, když přejdeme k vkládání a aktualizaci produktů pomocí objektu 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 chybějící výchozí konstruktor 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 s Strongly-Typed datových sad a Problém s ObjectDataSource a Strongly-Typed DataSet.

Dále v systémech a AddProductUpdateProductvytvoří kód ProductsRow 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 kontrolám ověření 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. Pokud chcete označit, že konkrétní sloupec DataColumn v objektu DataRow by měl odpovídat hodnotě NULL databáze, musíme použít metodu SetColumnNameNull() .

V UpdateProduct nástroji nejprve načteme produkt, který aktualizujeme 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 v budoucích kurzech, které se zabývají optimistickou souběžností, se osvědčí. 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ž třídu prozkoumáme SuppliersBLL , 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, ASP.NET ObjectDataSource 2.0 usnadňuje deklarativně přístup k datům z třídy. Aby bylo možné filtrovat seznam možných tříd, které se mají navá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í usnadní práci s v průvodci ObjectDataSource.

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

ProductsBLL Po dokončení třídy stále potřebujeme přidat třídy pro práci s kategoriemi, dodavateli a zaměstnanci. Vytvořte následující třídy a metody pomocí 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)

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 načte objekt pro zadaný supplierID objekt (pomocí GetSupplierBySupplierID), nastaví jeho vlastnosti související s adresou a pak zavolá metodu SupplierDataTableUpdate .SupplierDataRow Metoda UpdateSupplierAddress je následující:

[System.ComponentModel.DataObjectMethodAttribute
    (System.ComponentModel.DataObjectMethodType.Update, true)]
public bool UpdateSupplierAddress
    (int supplierID, string address, string city, string country)
{
    Northwind.SuppliersDataTable suppliers =
        Adapter.GetSupplierBySupplierID(supplierID);
    if (suppliers.Count == 0)
        // no matching record found, return false
        return false;
    else
    {
        Northwind.SuppliersRow supplier = suppliers[0];

        if (address == null) supplier.SetAddressNull();
          else supplier.Address = address;
        if (city == null) supplier.SetCityNull();
          else supplier.City = city;
        if (country == null) supplier.SetCountryNull();
          else supplier.Country = country;

        // Update the supplier Address-related information
        int rowsAffected = Adapter.Update(supplier);

        // Return true if precisely one row was updated,
        // otherwise false
        return rowsAffected == 1;
    }
}

Kompletní implementaci tříd BLL najdete v tomto článku ke stažení.

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

V prvním kurzu jsme viděli příklady přímé práce s typed dataSet programově, ale s přidáním našich tříd BLL by prezentační vrstva měla místo toho fungovat 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:

ProductsTableAdapter productsAdapter = new ProductsTableAdapter();
GridView1.DataSource = productsAdapter.GetProducts();
GridView1.DataBind();

Pokud chcete použít nové třídy BLL, stačí, když první řádek kódu jednoduše nahradí ProductsTableAdapter objekt objektem ProductBLL :

ProductsBLL productLogic = new ProductsBLL();
GridView1.DataSource = productLogic.GetProducts();
GridView1.DataBind();

Třídy BLL lze také přistupovat deklarativně (stejně jako Typed DataSet) pomocí ObjectDataSource. Objektu ObjectDataSource se budeme podrobněji věnovat v následujících kurzech.

The List of Products is Displayed in a 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 polí 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.cs.

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.cs

public partial class Northwind
{
    public partial class ProductsDataTable
    {
        public override void BeginInit()
         {
            this.ColumnChanging += ValidateColumn;
         }

         void ValidateColumn(object sender,
           DataColumnChangeEventArgs e)
         {
            if(e.Column.Equals(this.UnitPriceColumn))
            {
               if(!Convert.IsDBNull(e.ProposedValue) &&
                  (decimal)e.ProposedValue < 0)
               {
                  throw new ArgumentException(
                      "UnitPrice cannot be less than zero", "UnitPrice");
               }
            }
            else if (e.Column.Equals(this.UnitsInStockColumn) ||
                     e.Column.Equals(this.UnitsOnOrderColumn) ||
                     e.Column.Equals(this.ReorderLevelColumn))
            {
                if (!Convert.IsDBNull(e.ProposedValue) &&
                    (short)e.ProposedValue < 0)
                {
                    throw new ArgumentException(string.Format(
                        "{0} cannot be less than zero", e.Column.ColumnName),
                        e.Column.ColumnName);
                }
            }
         }
    }
}

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.

public bool UpdateProduct(string productName, int? supplierID, int? categoryID,
    string quantityPerUnit, decimal? unitPrice, short? unitsInStock,
    short? unitsOnOrder, short? reorderLevel, bool discontinued, int productID)
{
    Northwind.ProductsDataTable products = Adapter.GetProductByProductID(productID);
    if (products.Count == 0)
        // no matching record found, return false
        return false;

    Northwind.ProductsRow product = products[0];

    // Business rule check - cannot discontinue
    // a product that is supplied by only
    // one supplier
    if (discontinued)
    {
        // Get the products we buy from this supplier
        Northwind.ProductsDataTable productsBySupplier =
            Adapter.GetProductsBySupplierID(product.SupplierID);

        if (productsBySupplier.Count == 1)
            // this is the only product we buy from this supplier
            throw new ApplicationException(
                "You cannot mark a product as discontinued if it is the only
                  product purchased from a supplier");
    }

    product.ProductName = productName;
    if (supplierID == null) product.SetSupplierIDNull();
      else product.SupplierID = supplierID.Value;
    if (categoryID == null) product.SetCategoryIDNull();
      else product.CategoryID = categoryID.Value;
    if (quantityPerUnit == null) product.SetQuantityPerUnitNull();
      else product.QuantityPerUnit = quantityPerUnit;
    if (unitPrice == null) product.SetUnitPriceNull();
      else product.UnitPrice = unitPrice.Value;
    if (unitsInStock == null) product.SetUnitsInStockNull();
      else product.UnitsInStock = unitsInStock.Value;
    if (unitsOnOrder == null) product.SetUnitsOnOrderNull();
      else product.UnitsOnOrder = unitsOnOrder.Value;
    if (reorderLevel == null) product.SetReorderLevelNull();
      else product.ReorderLevel = reorderLevel.Value;
    product.Discontinued = discontinued;

    // Update the product record
    int rowsAffected = Adapter.Update(product);

    // Return true if precisely one row was updated,
    // otherwise false
    return rowsAffected == 1;
}

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 ... blok catch , jak ukazuje následující příklad:

ProductsBLL productLogic = new ProductsBLL();

// Update information for ProductID 1
try
{
    // This will fail since we are attempting to use a
    // UnitPrice value less than 0.
    productLogic.UpdateProduct(
        "Scott s Tea", 1, 1, null, -14m, 10, null, null, false, 1);
}
catch (ArgumentException ae)
{
    Response.Write("There was a problem: " + ae.Message);
}

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.