(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

Работа с данными в ASP.NET. Создание уровня бизнес-логики

Источник: liveinternet

Предисловие

Это вторая статья из серии "Работа с Данными в ASP.NET". В данной статье мы рассмотрим создание в проиложении уровня бизнес-логики. Это очень важный момент при создании сложных приложений, т.к. именно в уровне бизнес логики осуществляются таки необходимые процессы, как авторизация, проверка введенных данных и т.п. За основу статьи взят материал Скота Митчелла "Creating a Business Logic Layer".

Введение

В первой статье мы с вами создали уровень доступа к данным (DAL), который выносил логику доступа к данным из уровня представления. Но это не позволит нам применить какие-либо бизнес правила. Например, нам нужно запретить возможность изменения полей CategoryID и SupplierID таблицы Products если поле Discontinued равно 1. Другой пример - авторизация пользователей.

Из данной статьи вы узнаете как централизовать всю бизнес логику приложения в специальном уровне - уровне бизнес логики (Business Logic Layer, BLL). BLL располагается между уровнем представления и уровнем доступа к данным

В реальных приложениях уровень бизнес логикип редставляет собой отдельный проект Class Library. В данной статье мы создадим серию файлов классов директории App_Code для упрощения структуры проекта.

Шаг 1. Создание классов бизнес логики

Наш BLL будет состоять из четырех классов, по одному на каждый TableAdapter в DAL. Каждый класс будет иметь набор методов для извлечения, изменения, добавления или удаления данных из соответствующего TableAdapter.

Для более наглядного разделения DAL и BLL - создайте две директории в папке App_Code, DAL и BLL и переместите Typed DataSet, созданный в первой статье в директорию DAL.

Теперь создайте четыре класса в директории BLL, это соответственно классы ProductsBLL, CategoriesBLL, SuppliersBLL и EmployeesBLL.

Сейчас мы добавим все необходимые методы в наши классы, позже мы вернемся и добавим необхоимую бизнес логику.

Примечание. Если у вас утановлена Visual Studio Standard Edition, или выше, вы так же можете воспользоваться утилитой Class Designer

В класс ProductsBLL нам нужно добавить семь методов:

  • GetProducts()
  • GetProductByProductID(productID)
  • GetProductsByCategoryID(categoryID)
  • GetProductsBySupplierID(supplierID)
  • AddProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued, productID)
  • UpdateProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued, productID)
  • DeleteProduct(productID)

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
       return rowsAffected == 1;
   }
}

Методы, которые просто возвращают данные - GetProducts, GetProductByProductID, GetProductsByCategoryID и GetProductBySuppliersID - просто обращаются к DAL. Иногда в таких методах необходимо добавить бизнес-правила, например только авторизованные пользователи могут просмотреть количество товара на складе. Мы оставим их как есть.  BLL для этих методов будет просто связующим звеном, между уровнем представления и DAL.

Методы AddProduct и UpdateProduct принимают аргументы, необходимые для обновления или добавления продукта. Значения многих полей таблицы Products могут принимать нулевые значения (NULL).  В C# можно указать, какие аргументы могут принимать нулевые значения. Делается это с помощью указания комплятору нулевого типа, для этого нужно поставить знак вопроса после названия типа (например, int? x;)

Все три последних метода возвращают значение типа bool, которое показывает удачно ли выполнена операция. Например, когда пользователь обращается к методу Delete и указывает насуществующий ProductID, то на выходе он получит false.

Заметьте, что при добавлении или изменении товара мы сначала создаем объект типа ProductsRow, произошедший от класса ADO.NET DataRow, и не имеющий своего конструктора. Так же все значения добавляются в ProductsRow вручную, а в случае нуля выполняется функция SetColumnNameNull(), что позволяет быть увереным в "правильности" ProductsRow.

В методе UpdateProduct мы сначала с помощью метода GetProductByProductID() получаем нужный товар, затем его изменяем, и затем вносим изменения в базу данных.

И наконец, заметьте, что перед каждым методом идет описание этого метода, это даст нам возможность использовать его в качестве ObjectDataSource

Добавление прочих классов

Мы с вами написали все методы для класса ProductsBLL, теперь вы самостоятельно можете написать остальные классы с методами. Вот их список:

  • CategoriesBLL.cs

o    GetCategories()

o    GetCategoryByCategoryID( categoryID )

  • SuppliersBLL.cs

o    GetSuppliers()

o    GetSupplierBySupplierID( supplierID )

o    GetSuppliersByCountry( country )

o    UpdateSupplierAddress( supplierID , address , city , country )

  • EmployeesBLL.cs

o    GetEmployees()

o    GetEmployeeByEmployeeID( employeeID )

o    GetEmployeesByManager( managerID )

Все вышеперечисленные функции вы можете написать самостоятельно, глада на пример класса ProductsBLL, но один метод из перечисленных мне необходимо описать, это метод 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;
   }
}

Шаг 2. Доступ к Typed DataSet с помощью BLL.

В первой статье мы с вами создали страничку, на которой программно использовали Typed DataSet в качаестве источника данных. Код был такой:

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

Теперь сделаем то же самое с использованием BLL:

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

Результат будут точно таким же. Но теперь у вас есть возможность включить в метод выборки бизнес правила.

Шаг 3. Добавление в классы проверки уровня полей.

В большинстве таблиц значения полей должны отвечать определенным требованиям, вот некоторые примеры:

  • ProductName должно быть не длинне 40 символов
  • QuantityPerUnit должно быть не длинне 20 символов
  • ProductID, ProductName и Discontinued являются обязательными полями, а все остальные - необязательными.
  • UnitPrice, UnitsInStock, UnitsOnOrder и ReorderLevel должны быть ольшими нуля.

Эти требования могут и должны быть вписаны в самой базе данных. Так же для надежности можно эти правила внести в уровень DataSet. Как это сделать, вы можете увидеть на рисунке:

К сожалению, с помощью окна Properties мы не можем создать проверку, такую как значение поля UnitPrice должно быть равным или больше нуля. Для того, чтобы осуществлять такую проверку нам необходимо создать обработчик события ColumnChanging. Как мы узнали из предыдущей статьи, мы можем создавать partial классы существующих классов. Используя такую технику мы сейчас определим обработчик события ColimnChanging. Для этого создадим в директории App_Code файл ProductsDataTable.ColumnChanging.cs

Теперь напишем обработчик события ColumnChanging, в котором проверим, чтобы значения полей UnitPrice, UnitsInStock, UnitsOnOrder и ReorderLevel, если они не равны NULL, были бы больше или равны нулю:

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);

                }

            }

        }

    }

}

Шаг 4. Добавление собственных бизнес правил в BLL классы

Часто необходимо создать для приложения набор определенных правил, например:

  • Если товар закончился, то нельзя изменить его цену
  • Страна нахождения работника должна совпадать со страной нахождения менеджера
  • Невозможно приостановить продажу товара, если это единственный товар купленный у поставщика

За проверку соблюдения таких правил и отвечает BLL.

Рассмотрим все на примере. Предположим, что у нас есть бизнес-правило, согласно которому продажа товара не может быть приостановлена, если это единственный поставляемый товар от конкретного поставщика. То есть, есть товар А, единственный товар, который поставляет поставщик Б, мы не можем приостановить продажу товара А, кроме как если поставщик Б не поставит нам еще и товары В, Г и Д, тогда мы сможем приостановить продажу любого из этих продуктов.

Для соблюдения этого правила мы изменим код метода UpdateProducts. Если свойство discontinued установлено в true, и если поставщик, который поставляет нам этот товар не поставляет больше никаких других товаров, то будет выполнено исключение приложения (ApplicationException)

    [System.ComponentModel.DataObjectMethod(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.GetProductsByProductID(ProductID);

        if (products.Count == 0)

            return false;

        Northwind.ProductsRow product = products[0];

        if (Discontinued)

        {

            Northwind.ProductsDataTable productsBySupplier = Adapter.GetProductsBySupplierID(product.SupplierID);

            if (productsBySupplier.Count == 1)

                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;

        int rowsAffected = Adapter.Update(product);

        return rowsAffected == 1;

    }

 

Отслеживание ошибок на уровне приложения

Для более стабильной работы придложения стоит все "неустойчивые" коды обрамлять в блок catch…try, и в случае ошибки обрабатывать исключиение.

        ProductsBLL productLogic = new ProductsBLL();

        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);

        }

Заключение

Хорошее приложение всегда должно быть разделено на несколько уровней, каждый из которых должен выполнять определенную роль. В данной статье мы рассмотрели уровень бизнес логики, в котором определяются бизнес правила приложения. Мы реализовали это путем создания нескольких классов, для каждого TableAdapter. Обычно вся бизнес логика выносится в отдельный проект ClassLibrary.

Теперь у нас готовы уровни доступа к данным и уровень бизнес логики и мы вполне можем приступить к созданию уровня представления. Чем и займемся в следующей статье =)

Ссылки по теме


 Распечатать »
 Правила публикации »
  Написать редактору 
 Рекомендовать » Дата публикации: 24.11.2009 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
Microsoft Office 365 Профессиональный Плюс. Подписка на 1 рабочее место на 1 год
Microsoft 365 Business Standard (corporate)
Microsoft Office 365 Персональный 32-bit/x64. 1 ПК/MAC + 1 Планшет + 1 Телефон. Все языки. Подписка на 1 год.
Microsoft Windows Professional 10, Электронный ключ
Microsoft 365 Apps for business (corporate)
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Безопасность компьютерных сетей и защита информации
Новости ITShop.ru - ПО, книги, документация, курсы обучения
Программирование на Microsoft Access
CASE-технологии
Новые материалы
Windows и Office: новости и советы
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100