Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этом руководстве мы посмотрим, как централизировать бизнес-правила на уровне бизнес-логики (BLL), который служит посредником для обмена данными между уровнем презентации и DAL.
Введение
Уровень доступа к данным (DAL), созданный в первом руководстве , четко отделяет логику доступа к данным от логики презентации. Однако, хотя DAL чисто отделяет сведения о доступе к данным от слоя презентации, он не применяет какие-либо бизнес-правила, которые могут применяться. Например, для нашего приложения может потребоваться запретить изменение полей CategoryID
или SupplierID
в таблице Products
, если для поля Discontinued
задано значение 1, или нам может потребоваться применить правила иерархии, запрещая ситуации, когда сотрудником управляет тот, кто был нанят позже него. Другой распространенный сценарий — авторизация, возможно, только пользователи определенной роли могут удалять продукты или изменять UnitPrice
значение.
В этом руководстве мы посмотрим, как централизировать эти бизнес-правила в уровне бизнес-логики (BLL), который служит посредником для обмена данными между уровнем презентации и DAL. В реальном приложении BLL следует реализовать как отдельный проект библиотеки классов; Однако для этих учебников мы реализуем BLL в виде ряда классов в нашей App_Code
папке, чтобы упростить структуру проекта. Рисунок 1 иллюстрирует архитектурные связи между уровнем презентации, BLL и DAL.
Рис. 1. BLL отделяет уровень презентации от уровня доступа к данным и накладывает бизнес-правила
Вместо создания отдельных классов для реализации бизнес-логики можно также поместить эту логику непосредственно в типизированный набор данных с частичными классами. Пример создания и расширения типизированного набора данных см. в первом руководстве.
Шаг 1. Создание классов BLL
Наш BLL будет состоять из четырех классов, по одному для каждого TableAdapter в DAL; каждый из этих классов BLL будет иметь методы для получения, вставки, обновления и удаления из соответствующего TableAdapter в DAL, применяя соответствующие бизнес-правила.
Чтобы более четко разделить классы, связанные с DAL и BLL, давайте создадим две вложенные папки в папке App_Code
DAL
и BLL
. Щелкните правой кнопкой мыши App_Code
папку в обозревателе решений и выберите команду "Создать папку". После создания этих двух папок переместите типизированный набор данных, созданный в первом руководстве, в вложенную папку DAL
.
Затем создайте четыре файла класса BLL в подпапке BLL
. Чтобы это сделать, щелкните правой кнопкой мыши на BLL
подпапке, выберите "Добавить новый элемент" и выберите шаблон класса. Назовите четыре класса ProductsBLL
, CategoriesBLL
и SuppliersBLL
EmployeesBLL
.
Рис. 2. Добавление четырех новых классов в папку App_Code
Далее давайте добавим методы к каждому из классов, чтобы просто упаковать методы, определенные для TableAdapters из первого руководства. Сейчас эти методы будут просто вызываться непосредственно в DAL; мы позже добавим необходимую бизнес-логику.
Замечание
Если вы используете Visual Studio Standard Edition или более поздней версии (т. е. вы не используете Visual Web Developer), вы можете при необходимости визуально разработать классы с помощью конструктора классов. Дополнительные сведения об этой новой функции в Visual Studio см. в блоге конструктора классов .
ProductsBLL
Для класса необходимо добавить в общей сложности семь методов:
-
GetProducts()
возвращает все продукты -
GetProductByProductID(productID)
возвращает продукт с указанным идентификатором продукта -
GetProductsByCategoryID(categoryID)
возвращает все продукты из указанной категории -
GetProductsBySupplier(supplierID)
возвращает все продукты из указанного поставщика -
AddProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued)
вставляет новый продукт в базу данных с использованием переданных значений;ProductID
возвращает значение только что вставленной записи. -
UpdateProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued, productID)
обновляет существующий продукт в базе данных с помощью переданных значений; возвращает значениеTrue
, если была обновлена ровно одна строка,False
в противном случае -
DeleteProduct(productID)
удаляет указанный продукт из базы данных
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
Методы, которые просто возвращают данные GetProducts
, GetProductByProductID
, GetProductsByCategoryID
и GetProductBySuppliersID
, довольно просты, так как они просто обращаются к DAL. Хотя в некоторых сценариях могут быть бизнес-правила, которые должны быть реализованы на этом уровне (например, правила авторизации на основе текущего пользователя или роли, к которой принадлежит пользователь), мы просто оставим эти методы as-is. Для этих методов BLL служит лишь прокси-сервером, через который слой презентации обращается к базовым данным из уровня доступа к данным.
Методы AddProduct
и UpdateProduct
принимают в качестве параметров значения для различных полей продукта и соответственно добавляют новый продукт или обновляют существующий. Так как многие из Product
столбцов таблицы могут принимать NULL
значения (CategoryID
, SupplierID
и UnitPrice
, чтобы назвать несколько), те входные параметры для AddProduct
и UpdateProduct
, которые соответствуют таким столбцам, используют типы, допускающие значение NULL. Типы, допускающие значение NULL, появились в .NET 2.0 и предоставляют способ указать, должен ли тип значения вместо этого быть Nothing
. Дополнительные сведения см. в записи блога Пола ВикаThe Truth About Nullable Types and VB и в технической документации по структуре Nullable.
Все три метода возвращают логическое значение, указывающее, была ли строка вставлена, обновлена или удалена, так как операция не может привести к затронутой строке. Например, если разработчик страницы вызывает DeleteProduct
, передавая ProductID
для несуществующего продукта, DELETE
SQL-запрос, отправленный в базу данных, не повлияет, и поэтому метод DeleteProduct
вернет False
.
Заметьте, что при добавлении нового продукта или обновлении существующего мы принимаем значения полей нового или измененного продукта в виде списка скаляров, а не экземпляра ProductsRow
. Этот подход был выбран, так как ProductsRow
класс является производным от класса ADO.NET DataRow
, который не имеет конструктора без параметров по умолчанию. Чтобы создать новый экземпляр ProductsRow
, сначала необходимо создать экземпляр ProductsDataTable
, а затем вызвать его метод NewProductRow()
(что мы делаем в AddProduct
). Этот недостаток проявляется при вставке и обновлении товаров с помощью ObjectDataSource. Короче говоря, ObjectDataSource попытается создать экземпляр входных параметров. Если метод BLL ожидает экземпляр ProductsRow
, то объект ObjectDataSource попытается создать его, но не сможет из-за отсутствия конструктора без параметров. Дополнительные сведения об этой проблеме см. в следующих двух ASP.NET форумах: обновление ObjectDataSources с помощью наборов данных Strongly-Typedи проблемы с ObjectDataSource и Strongly-Typed DataSet.
Затем в обоих AddProduct
UpdateProduct
случаях код создает ProductsRow
экземпляр и заполняет его только что переданными значениями. При назначении значений DataColumns в DataRow могут выполняться различные проверки на уровне поля. Поэтому ручное размещение переданных значений в DataRow помогает обеспечить допустимость передаваемых данных методу BLL. К сожалению, строго типизированные классы DataRow, созданные Visual Studio, не используют типы, допускающие значение NULL. Чтобы указать, что определенный столбец DataColumn в DataRow должен соответствовать NULL
значению базы данных, для которого необходимо использовать метод SetColumnNameNull()
.
Сначала в UpdateProduct
мы загружаем продукт для его обновления с помощью GetProductByProductID(productID)
. Хотя это может показаться ненужной поездкой в базе данных, эта дополнительная поездка оправдает себя в будущих учебных материалах, которые изучают оптимистичную конкуренцию. Оптимистическая конкуренция — это метод, который обеспечивает, чтобы два пользователя, работающие одновременно над одними и теми же данными, не перезаписывали случайно изменения друг друга. Захват всей записи также упрощает создание методов обновления в BLL, которые изменяют только подмножество столбцов DataRow. При изучении SuppliersBLL
класса мы увидим такой пример.
Наконец, обратите внимание, что для класса ProductsBLL
применяется атрибут DataObject (синтаксис [System.ComponentModel.DataObject]
появляется прямо перед объявлением класса в верхней части файла), а методы содержат атрибуты DataObjectMethodAttribute. Атрибут DataObject
помечает класс как объект, подходящий для привязки к элементу управления ObjectDataSource, в то время как DataObjectMethodAttribute
указывает назначение метода. Как мы увидим в будущих руководствах, ASP.NET 2.0 ObjectDataSource упрощает декларативный доступ к данным из класса. Чтобы отфильтровать список возможных классов для привязки к мастеру ObjectDataSource, по умолчанию только те классы, помеченные как DataObjects
показано в раскрывающемся списке мастера. Класс ProductsBLL
будет работать так же хорошо без этих атрибутов, но добавление их упрощает работу с мастером ObjectDataSource.
Добавление других классов
ProductsBLL
После завершения класса нам по-прежнему нужно добавить классы для работы с категориями, поставщиками и сотрудниками. Пожалуйста, уделите немного времени, чтобы создать следующие классы и методы, используя понятия из приведенного выше примера.
CategoriesBLL.cs
GetCategories()
GetCategoryByCategoryID(categoryID)
SuppliersBLL.cs
GetSuppliers()
GetSupplierBySupplierID(supplierID)
GetSuppliersByCountry(country)
UpdateSupplierAddress(supplierID, address, city, country)
EmployeesBLL.cs
GetEmployees()
GetEmployeeByEmployeeID(employeeID)
GetEmployeesByManager(managerID)
Метод, который стоит отметить, — это метод SuppliersBLL
класса UpdateSupplierAddress
. Этот метод предоставляет интерфейс для обновления только сведений об адресе поставщика.
SupplierDataRow
считывается этим методом для указанного supplierID
(с помощью GetSupplierBySupplierID
), задаются его свойства, связанные с адресом, а затем он вызывает метод SupplierDataTable
объекта Update
. Метод UpdateSupplierAddress
следующий:
<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
Ознакомьтесь с скачиванием этой статьи для полной реализации классов BLL.
Шаг 2. Доступ к типизированным наборам данных через классы BLL
В первом руководстве мы видели примеры работы непосредственно с Типизированным набором данных программным способом, но при добавлении классов BLL уровень презентации должен работать с BLL вместо этого.
AllProducts.aspx
В примере из первого руководства ProductsTableAdapter
использовался для привязки списка продуктов к GridView, как показано в следующем коде:
Dim productsAdapter As New ProductsTableAdapter()
GridView1.DataSource = productsAdapter.GetProducts()
GridView1.DataBind()
Чтобы использовать новые классы BLL, все, что необходимо изменить, является первой строкой кода, просто замените ProductsTableAdapter
объект объектом ProductBLL
:
Dim productLogic As New ProductsBLL()
GridView1.DataSource = productLogic.GetProducts()
GridView1.DataBind()
Классы BLL также могут быть доступны декларативно (как и типизированный набор данных) с помощью ObjectDataSource. Мы обсудим ObjectDataSource более подробно в рамках следующих руководств.
Рис. 3. Список продуктов отображается в GridView (щелкните, чтобы просмотреть изображение полного размера)
Шаг 3. Добавление проверки Field-Level в классы DataRow
Проверка на уровне поля — это проверки, относящиеся к значениям свойств бизнес-объектов при вставке или обновлении. Ниже перечислены некоторые правила проверки на уровне поля для продуктов:
- Поле
ProductName
должно содержать 40 символов или меньше длины. - Поле
QuantityPerUnit
должно содержать 20 символов или меньше длины. - Обязательными являются поля
ProductID
,ProductName
иDiscontinued
, но все остальные поля необязательны. - Поля
UnitPrice
,UnitsInStock
,UnitsOnOrder
иReorderLevel
должны быть больше или равно нулю
Эти правила могут быть выражены на уровне базы данных. Ограничение символов для ProductName
QuantityPerUnit
полей фиксируется типами данных этих столбцов в Products
таблице (nvarchar(40)
и nvarchar(20)
соответственно). Указывает, являются ли поля обязательными и необязательными, если столбец таблицы базы данных допускает NULL
s. Четыре ограничения существуют, чтобы гарантировать, что только значения, превышающие или равные нулю, могут попасть в столбцы UnitPrice
, UnitsInStock
, UnitsOnOrder
или ReorderLevel
.
Помимо применения этих правил в базе данных, они также должны быть применены на уровне Набора данных. Фактически, длина поля, а также требуемость или необязательность значения уже зафиксированы для каждого набора DataColumns таблицы данных (DataTable). Чтобы просмотреть автоматическую проверку на уровне поля, перейдите в конструктор наборов данных, выберите поле из одной из наборов данных, а затем перейдите в окно "Свойства". Как показано на рисунке 4, QuantityPerUnit
DataColumn в ProductsDataTable
имеет максимальную длину 20 символов и допускает NULL
значения. Если мы попытаемся установить для ProductsDataRow
QuantityPerUnit
строковое значение длиной более 20 символов, будет выдано исключение ArgumentException
.
Рис. 4: DataColumn предоставляет базовую проверку Field-Level (нажмите, чтобы увеличить изображение)
К сожалению, невозможно указать проверки границ, например UnitPrice
значение должно быть больше нуля или равно нулю в окне "Свойства". Чтобы предоставить этот тип проверки на уровне поля, необходимо создать обработчик событий для события ColumnChanging dataTable. Как упоминалось в предыдущем руководстве, объекты DataSet, DataTables и DataRow, созданные типизированным набором данных, можно расширить с помощью частичных классов. С помощью этого метода можно создать ColumnChanging
обработчик событий для ProductsDataTable
класса. Начните с создания класса в папке App_Code
с именем ProductsDataTable.ColumnChanging.vb
.
Рис. 5. Добавление нового класса в App_Code
папку (щелкните, чтобы просмотреть изображение полного размера)
Затем создайте обработчик событий для события ColumnChanging
, который гарантирует, что значения столбцов UnitPrice
, UnitsInStock
, UnitsOnOrder
и ReorderLevel
(если отсутствуют NULL
) больше или равны нулю. Если любой такой столбец выходит за пределы диапазона, бросьте 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
Шаг 4. Добавление пользовательских бизнес-правил в классы BLL
В дополнение к проверке на уровне полей могут быть высокоуровневые пользовательские бизнес-правила, которые включают различные сущности или понятия, которые не выражаются на уровне одного столбца, например:
- Если продукт прекращен, его
UnitPrice
невозможно обновить - Страна проживания сотрудника должна совпадать с страной проживания своего руководителя
- Продукт не может быть прекращен, если он является единственным продуктом, предоставленным поставщиком
Классы BLL должны содержать проверки, чтобы обеспечить соблюдение бизнес-правил приложения. Эти проверки можно непосредственно добавить в методы, к которым они применяются.
Представьте себе, что наши бизнес-правила диктуют, что продукт не может быть помечен как снятый с производства, если это был единственный продукт от данного поставщика. То есть, если продукт X был единственным продуктом, приобретенным у поставщика Y, мы не могли пометить X как прекращенный; Если, однако, поставщик Y предоставил нам три продукта , A, B и C, то мы могли бы пометить все и все из них как прекращенные. Странное бизнес-правило, но бизнес-правила и здравый смысл не всегда совпадают!
Чтобы применить это бизнес-правило в методе UpdateProducts
, мы начнем с проверки того, задано ли Discontinued
значение True
и, если да, мы будем вызывать GetProductsBySupplierID
, чтобы определить, сколько продуктов мы приобрели у поставщика этого продукта. Если у этого поставщика приобретается только один продукт, мы бросаем 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
Реагирование на ошибки проверки на уровне презентации
При взаимодействии с BLL на уровне представления мы можем решить, следует ли пытаться обрабатывать любые исключения, которые могут возникнуть, или позволить им распространиться до уровня ASP.NET (что приведет к возникновению события HttpApplication
Error
). Для обработки исключения при работе с BLL программным способом мы можем использовать блок Try...Catch, как показано в следующем примере.
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
Как мы увидим в будущих руководствах, исключения, которые возникают в BLL при использовании веб-контрола для вставки, обновления или удаления данных, можно обрабатывать непосредственно в обработчике событий, а не оборачивать код в блоки Try...Catch
.
Сводка
Хорошо архитекторированное приложение создается на отдельных уровнях, каждый из которых инкапсулирует определенную роль. В первом руководстве этой серии статей мы создали уровень доступа к данным с помощью типизированных наборов данных; в этом руководстве мы построили уровень бизнес-логики в виде ряда классов в папке нашего приложения App_Code
, которые обращаются к нашему DAL. BLL реализует логику уровня полей и бизнес-уровня для нашего приложения. Помимо создания отдельного BLL, как мы сделали в этом руководстве, еще один вариант — расширить методы TableAdapters с помощью частичных классов. Однако использование этого метода не позволяет нам перекрывать существующие методы и отделять наш DAL и наш BLL так же чисто, как описанный подход в этой статье.
После завершения DAL и BLL мы готовы начать работу на нашем уровне презентации. В следующем руководстве мы рассмотрим краткий переход из разделов доступа к данным и определим согласованный макет страницы для использования во всех руководствах.
Счастливое программирование!
Сведения о авторе
Скотт Митчелл, автор семи книг ASP/ASP.NET и основатель 4GuysFromRolla.com, работает с технологиями Microsoft Web с 1998 года. Скотт работает независимым консультантом, тренером и писателем. Его последняя книга — Sams Teach Yourself ASP.NET 2.0 за 24 часа. С ним можно связаться по адресу mitchell@4GuysFromRolla.com.
Особое спасибо кому
Эта серия учебников была проверена многими полезными рецензентами. Ведущими рецензентами этого учебника были Лиз Шулок, Деннис Паттерсон, Карлос Сантос и Хилтон Гизенау. Хотите просмотреть мои предстоящие статьи MSDN? Если да, напишите мне на mitchell@4GuysFromRolla.com.