Udostępnij za pośrednictwem


Tworzenie warstwy logiki biznesowej (C#)

Autor : Scott Mitchell

Pobierz plik PDF

W tym samouczku zobaczymy, jak scentralizować reguły biznesowe w warstwie logiki biznesowej (BLL), która służy jako pośrednik w wymianie danych między warstwą prezentacji a warstwą DAL.

Wprowadzenie

Warstwa dostępu do danych (DAL) utworzona w pierwszym samouczku wyraźnie oddziela logikę dostępu do danych od logiki prezentacji. Jednak podczas gdy funkcja DAL czysta oddziela szczegóły dostępu do danych od warstwy prezentacji, nie wymusza żadnych reguł biznesowych, które mogą być stosowane. Na przykład w przypadku naszej aplikacji możemy nie zezwalać CategoryID na modyfikowanie pól Products lub SupplierID tabeli, gdy Discontinued pole jest ustawione na 1, lub możemy chcieć wymusić reguły seniorstwa, zabraniając sytuacji, w których pracownik jest zarządzany przez kogoś, kto został zatrudniony po nich. Innym typowym scenariuszem jest autoryzacja, prawdopodobnie tylko użytkownicy w określonej roli mogą usuwać produkty lub zmieniać UnitPrice wartość.

W tym samouczku zobaczymy, jak scentralizować te reguły biznesowe w warstwie logiki biznesowej (BLL), która służy jako pośrednik w wymianie danych między warstwą prezentacji a warstwą DAL. W rzeczywistych aplikacjach BLL należy zaimplementować jako oddzielny projekt Biblioteki klas; Jednak w przypadku tych samouczków wdrożymy BLL jako serię klas w naszym App_Code folderze, aby uprościć strukturę projektu. Rysunek 1 przedstawia relacje architektury między warstwą prezentacji, BLL i DAL.

BLL oddziela warstwę prezentacji od warstwy dostępu do danych i nakłada reguły biznesowe

Rysunek 1. BLL oddziela warstwę prezentacji od warstwy dostępu do danych i nakłada reguły biznesowe

Krok 1. Tworzenie klas BLL

Nasza BLL będzie składać się z czterech klas, po jednym dla każdego tableAdapter w DAL; każda z tych klas BLL będzie zawierać metody pobierania, wstawiania, aktualizowania i usuwania z odpowiedniego obiektu TableAdapter w dal, stosując odpowiednie reguły biznesowe.

Aby dokładniej oddzielić klasy związane z dal i BLL, utwórzmy dwa podfoldery w folderze App_CodeDAL i BLL. Po prostu kliknij prawym przyciskiem myszy App_Code folder w Eksplorator rozwiązań i wybierz pozycję Nowy folder. Po utworzeniu tych dwóch folderów przenieś typowy zestaw danych utworzony w pierwszym samouczku do podfolderu DAL .

Następnie utwórz cztery pliki klas BLL w BLL podfolderze. Aby to osiągnąć, kliknij prawym przyciskiem myszy BLL podfolder, wybierz polecenie Dodaj nowy element i wybierz szablon Klasa. Nazwij cztery klasy ProductsBLL, CategoriesBLL, SuppliersBLLi EmployeesBLL.

Dodawanie czterech nowych klas do folderu App_Code

Rysunek 2. Dodawanie czterech nowych klas do App_Code folderu

Następnie dodajmy metody do każdej z klas, aby po prostu opakować metody zdefiniowane dla elementów TableAdapters z pierwszego samouczka. Na razie te metody będą po prostu wywoływać bezpośrednio do dal; Wrócimy później, aby dodać dowolną potrzebną logikę biznesową.

Uwaga

Jeśli używasz programu Visual Studio Standard Edition lub nowszego (czyli nie używasz programu Visual Web Developer), możesz opcjonalnie wizualnie zaprojektować klasy przy użyciu Projektant Klas. Aby uzyskać więcej informacji na temat tej nowej funkcji w programie Visual Studio, zapoznaj się z blogami Projektant klas.

ProductsBLL Dla klasy musimy dodać łącznie siedem metod:

  • GetProducts() zwraca wszystkie produkty
  • GetProductByProductID(productID) zwraca produkt o określonym identyfikatorze produktu
  • GetProductsByCategoryID(categoryID) Zwraca wszystkie produkty z określonej kategorii
  • GetProductsBySupplier(supplierID) zwraca wszystkie produkty od określonego dostawcy
  • AddProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued) Wstawia nowy produkt do bazy danych przy użyciu przekazanych wartości; ProductID Zwraca wartość nowo wstawionego rekordu
  • UpdateProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued, productID) aktualizuje istniejący produkt w bazie danych przy użyciu przekazanych wartości; metoda zwraca true wartość , jeśli został zaktualizowany dokładnie jeden wiersz, false w przeciwnym razie
  • DeleteProduct(productID) usuwa określony produkt z bazy danych

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, które po prostu zwracają dane GetProducts, GetProductByProductID, GetProductsByCategoryIDi GetProductBySuppliersID są dość proste, ponieważ po prostu są wywoływane do dal. W niektórych scenariuszach mogą istnieć reguły biznesowe, które muszą zostać zaimplementowane na tym poziomie (takie jak reguły autoryzacji oparte na aktualnie zalogowanym użytkowniku lub roli, do której należy użytkownik), po prostu pozostawimy te metody tak, jak jest. W przypadku tych metod BLL służy jedynie jako serwer proxy, za pośrednictwem którego warstwa prezentacji uzyskuje dostęp do danych bazowych z warstwy dostępu do danych.

Obie AddProduct metody i UpdateProduct przyjmują jako parametry wartości dla różnych pól produktu i dodają nowy produkt lub zaktualizują istniejący. Ponieważ wiele Product kolumn tabeli może akceptować NULL wartości (CategoryID, SupplierID, i UnitPrice, do kilku), te parametry wejściowe dla AddProduct i UpdateProduct mapowania na takie kolumny używają typów dopuszczających wartość null. Typy dopuszczające wartość null są nowe dla platformy .NET 2.0 i stanowią technikę wskazującą, czy typ wartości powinien mieć wartość null. W języku C# można oznaczyć typ wartości jako typ dopuszczalny do wartości null, dodając ? wartość po typie (na przykład int? x;). Aby uzyskać więcej informacji, zapoznaj się z sekcją Typy dopuszczane do wartości null w Przewodniku programowania w języku C# .

Wszystkie trzy metody zwracają wartość logiczną wskazującą, czy wiersz został wstawiony, zaktualizowany lub usunięty, ponieważ operacja może nie spowodować wystąpienia problemu w wierszu. Jeśli na przykład deweloper strony wywołuje DeleteProduct przekazanie ProductID elementu dla nieistniejącego produktu, DELETE instrukcja wystawiona dla bazy danych nie będzie miała wpływu i dlatego DeleteProduct metoda zwróci wartość false.

Należy pamiętać, że podczas dodawania nowego produktu lub aktualizowania istniejącego przyjmujemy wartości pól nowego lub zmodyfikowanego produktu jako listę skalarnych, w przeciwieństwie do akceptowania ProductsRow wystąpienia. To podejście zostało wybrane, ponieważ ProductsRow klasa pochodzi z klasy ADO.NETDataRow, która nie ma domyślnego konstruktora bez parametrów. Aby utworzyć nowe ProductsRow wystąpienie, musimy najpierw utworzyć ProductsDataTable wystąpienie, a następnie wywołać jego NewProductRow() metodę (co robimy w AddProductpliku ). To niedoścignięcie zmienia głowę, gdy zwracamy się do wstawiania i aktualizowania produktów przy użyciu obiektu ObjectDataSource. Krótko mówiąc, obiekt ObjectDataSource spróbuje utworzyć wystąpienie parametrów wejściowych. Jeśli metoda BLL oczekuje wystąpienia, obiekt ObjectDataSource spróbuje ProductsRow go utworzyć, ale zakończy się niepowodzeniem z powodu braku domyślnego konstruktora bez parametrów. Aby uzyskać więcej informacji na temat tego problemu, zapoznaj się z następującymi wpisami na forach ASP.NET: Aktualizowanie obiektówDataSources przy użyciu zestawów danych Strongly-Typed oraz Problem z obiektami ObjectDataSource i Strongly-Typed DataSet.

Następnie zarówno w AddProduct kodzie , jak i UpdateProductkod tworzy ProductsRow wystąpienie i wypełnia je wartościami, które zostały właśnie przekazane. Podczas przypisywania wartości do kolumny DataColumns obiektu DataRow mogą wystąpić różne sprawdzenia poprawności na poziomie pola. W związku z tym ręczne umieszczenie przekazanych wartości z powrotem do elementu DataRow pomaga zapewnić ważność danych przekazywanych do metody BLL. Niestety silnie typizowane klasy DataRow generowane przez program Visual Studio nie używają typów dopuszczalnych wartości null. Zamiast tego, aby wskazać, że określona kolumna DataColumn w obiekcie DataRow powinna odpowiadać NULL wartości bazy danych, należy użyć SetColumnNameNull() metody .

W UpdateProduct pliku najpierw załadujemy produkt do aktualizacji przy użyciu polecenia GetProductByProductID(productID). Chociaż może to wydawać się niepotrzebną podróżą do bazy danych, ta dodatkowa podróż okaże się opłacalna w przyszłych samouczkach, które eksplorują optymistyczną współbieżność. Optymistyczna współbieżność to technika zapewniająca, że dwóch użytkowników, którzy pracują jednocześnie nad tymi samymi danymi, nie zastępuje przypadkowo zmian. Pobranie całego rekordu ułatwia również tworzenie metod aktualizacji w usłudze BLL, które modyfikują tylko podzestaw kolumn datarow. Podczas eksplorowania SuppliersBLL klasy zobaczymy taki przykład.

Na koniec należy pamiętać, że klasa ProductsBLL ma zastosowany atrybut DataObject ( [System.ComponentModel.DataObject] składnia bezpośrednio przed instrukcją klasy w górnej części pliku), a metody mają atrybuty DataObjectMethodAttribute. Atrybut DataObject oznacza klasę jako obiekt odpowiedni do powiązania z kontrolką ObjectDataSource, natomiast DataObjectMethodAttribute element wskazuje cel metody . Jak zobaczymy w przyszłych samouczkach, ASP.NET 2.0's ObjectDataSource ułatwia deklaratywny dostęp do danych z klasy. Aby ułatwić filtrowanie listy możliwych klas do powiązania w kreatorze objectDataSource, domyślnie tylko te klasy oznaczone jako DataObjects są wyświetlane na liście rozwijanej kreatora. Klasa ProductsBLL będzie działać równie dobrze bez tych atrybutów, ale dodanie ich ułatwia pracę z kreatorem ObjectDataSource.

Dodawanie innych klas

ProductsBLL Po ukończeniu klasy nadal musimy dodać klasy do pracy z kategoriami, dostawcami i pracownikami. Poświęć chwilę na utworzenie następujących klas i metod przy użyciu pojęć z powyższego przykładu:

  • CategoriesBLL.cs

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

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

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

Jedyną metodą, która warto zauważyć, jest SuppliersBLL metoda klasy UpdateSupplierAddress . Ta metoda udostępnia interfejs do aktualizowania tylko informacji o adresie dostawcy. Ta metoda jest odczytywana wewnętrznie w SupplierDataRow obiekcie dla określonego supplierID (przy użyciu GetSupplierBySupplierID), ustawia jego właściwości związane z adresami, a następnie wywołuje metodę SupplierDataTablew dół do metody .s Update . Metoda jest następująca UpdateSupplierAddress :

[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;
    }
}

Zapoznaj się z pobieraniem tego artykułu, aby uzyskać pełną implementację klas BLL.

Krok 2. Uzyskiwanie dostępu do typowanych zestawów danych za pośrednictwem klas BLL

W pierwszym samouczku przedstawiono przykłady pracy bezpośrednio z typed DataSet programowo, ale wraz z dodaniu naszych klas BLL warstwa prezentacji powinna działać względem BLL. W przykładzie z AllProducts.aspx pierwszego samouczka ProductsTableAdapter użyto elementu , aby powiązać listę produktów z elementem GridView, jak pokazano w poniższym kodzie:

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

Aby użyć nowych klas BLL, wszystko, co należy zmienić, to pierwszy wiersz kodu po prostu zastąpić ProductsTableAdapter obiekt obiektem ProductBLL :

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

Dostęp do klas BLL można również uzyskać deklaratywnie (podobnie jak typed DataSet) przy użyciu obiektu ObjectDataSource. Bardziej szczegółowo omówimy obiekt ObjectDataSource w poniższych samouczkach.

Lista produktów jest wyświetlana w elementy GridView

Rysunek 3. Lista produktów jest wyświetlana w siatce (kliknij, aby wyświetlić obraz w pełnym rozmiarze)

Krok 3. Dodawanie weryfikacji Field-Level do klas DataRow

Sprawdzanie poprawności na poziomie pola dotyczy wartości właściwości obiektów biznesowych podczas wstawiania lub aktualizowania. Niektóre reguły weryfikacji na poziomie pola dla produktów to:

  • Pole ProductName musi mieć długość 40 znaków lub mniej
  • Pole QuantityPerUnit musi mieć długość 20 znaków lub mniej
  • Pola ProductID, ProductNamei Discontinued są wymagane, ale wszystkie inne pola są opcjonalne
  • Pola UnitPrice, UnitsInStock, UnitsOnOrderi ReorderLevel muszą być większe lub równe zero

Te reguły mogą i powinny być wyrażone na poziomie bazy danych. Limit znaków w polach ProductName i QuantityPerUnit jest przechwytywany przez typy danych tych kolumn w Products tabeli (nvarchar(40) i nvarchar(20), odpowiednio). Czy pola są wymagane i opcjonalne, są wyrażane przez kolumnę tabeli bazy danych.NULL Istnieją cztery ograniczenia sprawdzania, które zapewniają, że tylko wartości większe niż lub równe zero mogą przekształcić je w UnitPricekolumny , , UnitsInStockUnitsOnOrderlub ReorderLevel .

Oprócz wymuszania tych reguł w bazie danych należy je również wymuszać na poziomie zestawu danych. W rzeczywistości długość pola i to, czy wartość jest wymagana, czy opcjonalna, są już przechwytywane dla każdego zestawu kolumn Danych DataTable. Aby automatycznie wyświetlić istniejącą weryfikację na poziomie pola, przejdź do Projektant Zestawu danych, wybierz pole z jednej z tabel Danych, a następnie przejdź do okno Właściwości. Jak pokazano na rysunku 4, kolumna QuantityPerUnit DataColumn w obiekcie ProductsDataTable ma maksymalną długość 20 znaków i zezwala na NULL wartości. Jeśli spróbujemy ustawić ProductsDataRowwłaściwość 's QuantityPerUnit na wartość ciągu dłuższą niż 20 znaków, ArgumentException zostanie zgłoszonych.

Funkcja DataColumn zapewnia podstawową weryfikację Field-Level

Rysunek 4. Kolumna danych zapewnia podstawową weryfikację Field-Level (kliknij, aby wyświetlić obraz w pełnym rozmiarze)

Niestety, nie można określić kontroli granic, takich jak UnitPrice wartość musi być większa lub równa zero, za pośrednictwem okno Właściwości. Aby zapewnić ten typ weryfikacji na poziomie pola, należy utworzyć procedurę obsługi zdarzeń dla zdarzenia ColumnChanging tabeli DataTable. Jak wspomniano w poprzednim samouczku, obiekty DataSet, DataTables i DataRow utworzone przez typowy zestaw danych można rozszerzyć za pomocą klas częściowych. Korzystając z tej techniki, możemy utworzyć procedurę ColumnChanging obsługi zdarzeń dla ProductsDataTable klasy . Zacznij od utworzenia klasy w folderze App_Code o nazwie ProductsDataTable.ColumnChanging.cs.

Dodawanie nowej klasy do folderu App_Code

Rysunek 5. Dodawanie nowej klasy do App_Code folderu (kliknij, aby wyświetlić obraz pełnowymiarowy)

Następnie utwórz procedurę obsługi zdarzeń dla ColumnChanging zdarzenia, która gwarantuje, że UnitPricewartości kolumn , UnitsInStock, UnitsOnOrderi ReorderLevel (jeśli nie NULL) są większe lub równe zero. Jeśli jakakolwiek taka kolumna jest poza zakresem, wyrzuć element 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. Dodawanie niestandardowych reguł biznesowych do klas BLL

Oprócz weryfikacji na poziomie pola mogą istnieć niestandardowe reguły biznesowe wysokiego poziomu, które obejmują różne jednostki lub pojęcia, które nie są wyrażalne na poziomie pojedynczej kolumny, takie jak:

  • Jeśli produkt zostanie wycofany, nie można go UnitPrice zaktualizować
  • Kraj zamieszkania pracownika musi być taki sam jak kraj zamieszkania swojego kierownika
  • Produkt nie może zostać wycofany, jeśli jest to jedyny produkt dostarczony przez dostawcę

Klasy BLL powinny zawierać kontrole w celu zapewnienia zgodności z regułami biznesowymi aplikacji. Te kontrole można dodać bezpośrednio do metod, do których mają zastosowanie.

Załóżmy, że nasze zasady biznesowe określają, że produkt nie może zostać oznaczony jako zaprzestany, jeśli był to jedyny produkt od danego dostawcy. Oznacza to, że jeśli produkt X był jedynym produktem zakupionym od dostawcy Y, nie możemy oznaczyć produktu X jako wycofanego; jeśli jednak dostawca Y dostarczył nam trzy produkty, A, B i C, wówczas możemy oznaczyć wszystkie te produkty jako zaprzestane. Dziwna reguła biznesowa, ale reguły biznesowe i zdrowy rozsądek nie zawsze są dostosowane!

Aby wymusić tę regułę biznesową UpdateProducts w metodzie, zacznijmy od sprawdzenia, czy Discontinued ustawiono wartość true i, jeśli tak, wywołamy metodę GetProductsBySupplierID w celu określenia, ile produktów kupiliśmy od dostawcy tego produktu. W przypadku zakupu tylko jednego produktu od tego dostawcy zgłaszamy wyjątek 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;
}

Reagowanie na błędy walidacji w warstwie prezentacji

Podczas wywoływania biblioteki BLL z warstwy prezentacji możemy zdecydować, czy należy podjąć próbę obsługi wszelkich wyjątków, które mogą zostać zgłoszone, czy pozwolić im na ASP.NET (co spowoduje wywołanie HttpApplicationzdarzenia ).Error Aby obsłużyć wyjątek podczas programowej pracy z usługą BLL, możemy użyć polecenia wypróbuj... blok catch , jak pokazano w poniższym przykładzie:

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 zobaczymy w przyszłych samouczkach, obsługa wyjątków, które są bąbelkowe od BLL podczas używania kontrolki internetowej danych do wstawiania, aktualizowania lub usuwania danych, można obsługiwać bezpośrednio w procedurze obsługi zdarzeń, w przeciwieństwie do konieczności zawijania kodu w try...catch blokach.

Podsumowanie

Dobrze zaprojektowana aplikacja jest oparta na odrębnych warstwach, z których każda hermetyzuje określoną rolę. W pierwszym samouczku tej serii artykułów utworzyliśmy warstwę dostępu do danych przy użyciu typowych zestawów danych; W tym samouczku utworzyliśmy warstwę logiki biznesowej jako serię klas w folderze naszej aplikacji App_Code , które są wywoływane do naszej nazwy DAL. Usługa BLL implementuje logikę na poziomie pola i na poziomie biznesowym dla naszej aplikacji. Oprócz utworzenia oddzielnej BLL, tak jak w tym samouczku, inną opcją jest rozszerzenie metod TableAdapters za pomocą klas częściowych. Jednak użycie tej techniki nie pozwala nam przesłonić istniejących metod ani nie oddziela to naszego dal i naszego BLL tak czysto, jak podejście podjęte w tym artykule.

Po zakończeniu dal i BLL możemy rozpocząć pracę w warstwie prezentacji. W następnym samouczku zajmiemy się krótkim objazdem z tematów dostępu do danych i zdefiniujemy spójny układ strony do użycia w ramach samouczków.

Szczęśliwe programowanie!

Informacje o autorze

Scott Mitchell, autor siedmiu książek ASP/ASP.NET i założyciel 4GuysFromRolla.com, współpracuje z technologiami internetowymi firmy Microsoft od 1998 roku. Scott pracuje jako niezależny konsultant, trener i pisarz. Jego najnowsza książka to Sams Teach Yourself ASP.NET 2.0 w ciągu 24 godzin. Można do niego dotrzeć pod adresem mitchell@4GuysFromRolla.com. Lub za pośrednictwem swojego bloga, który można znaleźć na stronie http://ScottOnWriting.NET.

Specjalne podziękowania

Ta seria samouczków została przejrzyona przez wielu przydatnych recenzentów. Głównymi recenzentami tego samouczka byli Liz Shulok, Dennis Patterson, Carlos Santos i Hilton Giesenow. Chcesz przejrzeć nadchodzące artykuły MSDN? Jeśli tak, upuść mi wiersz pod adresemmitchell@4GuysFromRolla.com .