Работа с данными в 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 нам нужно добавить семь методов:
ProductsBLL.cs
Методы, которые просто возвращают данные - 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, теперь вы самостоятельно можете написать остальные классы с методами. Вот их список:
o GetCategories() o GetCategoryByCategoryID( categoryID )
o GetSuppliers() o GetSupplierBySupplierID( supplierID ) o GetSuppliersByCountry( country ) o UpdateSupplierAddress( supplierID , address , city , country )
o GetEmployees() o GetEmployeeByEmployeeID( employeeID ) o GetEmployeesByManager( managerID ) Все вышеперечисленные функции вы можете написать самостоятельно, глада на пример класса ProductsBLL, но один метод из перечисленных мне необходимо описать, это метод UpdateSupplierAddress():
Шаг 2. Доступ к Typed DataSet с помощью BLL.В первой статье мы с вами создали страничку, на которой программно использовали Typed DataSet в качаестве источника данных. Код был такой:
Теперь сделаем то же самое с использованием BLL:
Результат будут точно таким же. Но теперь у вас есть возможность включить в метод выборки бизнес правила.
Шаг 3. Добавление в классы проверки уровня полей.В большинстве таблиц значения полей должны отвечать определенным требованиям, вот некоторые примеры:
Эти требования могут и должны быть вписаны в самой базе данных. Так же для надежности можно эти правила внести в уровень 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. Теперь у нас готовы уровни доступа к данным и уровень бизнес логики и мы вполне можем приступить к созданию уровня представления. Чем и займемся в следующей статье =) |