Создание уровня бизнес-логики (VB)

Скотт Митчелл

Загрузить PDF-файл

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

Введение

Уровень доступа к данным (DAL), созданный в первом руководстве , четко отделяет логику доступа к данным от логики представления. Однако, хотя DAL четко отделяет сведения о доступе к данным от уровня представления, он не применяет бизнес-правила, которые могут применяться. Например, для нашего приложения может потребоваться запретить CategoryID изменение полей Products или SupplierID таблицы, если Discontinued для поля задано значение 1, или применить правила старшинства, запрещающие ситуации, когда сотрудник управляется кем-то, кто был нанят после них. Другой распространенный сценарий заключается в том, что только пользователи с определенной ролью могут удалять продукты или изменять UnitPrice значение.

В этом руководстве мы посмотрим, как централизовать эти бизнес-правила в уровне бизнес-логики (BLL), который служит посредником для обмена данными между уровнем представления и DAL. В реальном приложении BLL следует реализовать как отдельный проект библиотеки классов; Однако в этих руководствах мы реализуем BLL как серию классов в нашей App_Code папке, чтобы упростить структуру проекта. На рисунке 1 показаны архитектурные связи между уровнем представления, BLL и DAL.

BLL отделяет уровень представления от уровня доступа к данным и накладывает бизнес-правила.

Рис. 1. BLL отделяет уровень представления от уровня доступа к данным и накладывает бизнес-правила

Вместо создания отдельных классов для реализации бизнес-логики можно также поместить эту логику непосредственно в typed DataSet с разделяемыми классами. Пример создания и расширения typed DataSet см. в первом руководстве.

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

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

Чтобы более четко разделить классы, связанные с DAL и BLL, создадим две вложенные папки в папке App_Code и DALBLL. Просто щелкните правой кнопкой мыши папку App_Code в Обозреватель решений и выберите Создать папку. После создания этих двух папок переместите типизированный набор данных, созданный в первом руководстве, во вложенную папку DAL .

Затем создайте четыре файла класса BLL во вложенной BLL папке. Для этого щелкните правой BLL кнопкой мыши вложенную папку, выберите Добавить новый элемент и выберите шаблон Класс. Назовите четыре класса ProductsBLL, CategoriesBLL, SuppliersBLLи EmployeesBLL.

Добавление четырех новых классов в папку App_Code

Рис. 2. Добавление четырех новых классов в папку App_Code

Далее давайте добавим методы к каждому классу, чтобы просто упаковать методы, определенные для TableAdapters из первого учебника. Пока эти методы будут вызываться непосредственно в DAL; Мы вернемся позже, чтобы добавить любую необходимую бизнес-логику.

Примечание

Если вы используете Visual Studio Standard Edition или более поздней версии (то есть вы не используете Visual Web Developer), вы можете при необходимости визуально создать классы с помощью Designer класса. Дополнительные сведения об этой новой функции в Visual Studio см. в блоге о классах Designer.

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. Хотя в некоторых сценариях могут существовать бизнес-правила, которые необходимо реализовать на этом уровне (например, правила авторизации на основе вошедшего в систему пользователя или роли, к которой принадлежит пользователь), мы просто оставим эти методы как есть. Для этих методов BLL служит просто прокси-сервером, через который уровень представления обращается к базовым данным из уровня доступа к данным.

Оба AddProduct метода и UpdateProduct принимают в качестве параметров значения для различных полей продукта и добавляют новый продукт или обновляют существующий соответственно. Так как многие столбцы Product таблицы могут принимать NULL значения (CategoryID, SupplierIDи UnitPrice, чтобы назвать несколько), эти входные параметры для AddProduct и UpdateProduct , сопоставленные с такими столбцами, используют типы, допускающие значение NULL. Типы, допускающие значение NULL, являются новыми для .NET 2.0 и предоставляют метод определения того, должен ли тип значения иметь Nothingзначение . Дополнительные сведения см. в записи блога Пола ВикаThe Truth About Null Types and VB и технической документации по структуре, допускаемой значения NULL .

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

Обратите внимание, что при добавлении нового продукта или обновлении существующего мы принимаем значения полей нового или измененного продукта в виде списка скаляров, а не как для принятия экземпляра ProductsRow . Этот подход был выбран, так как ProductsRow класс является производным от класса ADO.NET DataRow , который не имеет конструктора без параметров по умолчанию. Чтобы создать новый ProductsRow экземпляр, необходимо сначала создать ProductsDataTable экземпляр, а затем вызвать его NewProductRow() метод (что мы делаем в AddProduct). Этот недостаток переползает, когда мы переходим к вставке и обновлению продуктов с помощью ObjectDataSource. Короче говоря, ObjectDataSource попытается создать экземпляр входных параметров. Если метод BLL ожидает ProductsRow экземпляр, ObjectDataSource попытается создать его, но завершится сбоем из-за отсутствия конструктора без параметров по умолчанию. Дополнительные сведения об этой проблеме см. в следующих двух публикациях на форумах ASP.NET: Обновление ObjectDataSources с помощью Strongly-Typed DataSets и Проблема с 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 ObjectDataSource 2.0 упрощает декларативный доступ к данным из класса. Чтобы упростить фильтрацию списка возможных классов для привязки в мастере 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), задает его свойства, связанные с адресом, а затем вызывает метод .SupplierDataTableUpdate Метод 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 также можно получить декларативно (как и typed DataSet) с помощью ObjectDataSource. Мы рассмотрим ObjectDataSource более подробно в следующих руководствах.

Список продуктов отображается в GridView

Рис. 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 базы данных. Существует четыре ограничения проверка, которые гарантируют, что только значения, превышающие или равные нулю, могут попасть в UnitPriceстолбцы , UnitsInStock, UnitsOnOrderили ReorderLevel .

Помимо применения этих правил в базе данных они также должны применяться на уровне DataSet. На самом деле длина поля и то, является ли значение обязательным или необязательным, уже записаны для набора DataColumns каждой таблицы DataTable. Чтобы просмотреть существующую проверку на уровне полей автоматически, перейдите на Designer DataSet, выберите поле из одной из данных DataTables и перейдите к окно свойств. Как показано на рисунке QuantityPerUnit 4, dataColumn в объекте ProductsDataTable имеет максимальную длину 20 символов и допускает NULL значения. При попытке ProductsDataRowQuantityPerUnit задать для свойства строковое значение, превышающее 20 символовArgumentException, будет выдано значение .

DataColumn обеспечивает базовую проверку Field-Level.

Рис. 4. DataColumn предоставляет базовую проверку Field-Level (щелкните для просмотра полноразмерного изображения)

К сожалению, мы не можем указать проверки границ, например UnitPrice значение должно быть больше или равно нулю через окно свойств. Чтобы обеспечить этот тип проверки на уровне поля, необходимо создать обработчик события ColumnChanging в DataTable. Как упоминалось в предыдущем руководстве, объекты DataSet, DataTables и DataRow, созданные typed DataSet, можно расширить с помощью разделяемых классов. С помощью этого метода можно создать ColumnChanging обработчик событий для ProductsDataTable класса . Начните с создания класса в папке App_Code с именем ProductsDataTable.ColumnChanging.vb.

Добавление нового класса в папку App_Code

Рис. 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 методе, мы сначала проверим, задано True ли Discontinued значение , и если да, мы вызовем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 (что вызовет HttpApplicationError событие ). Чтобы обработать исключение при работе с 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 мы готовы начать работу с уровнем презентации. В следующем руководстве мы кратко рассмотрим разделы, посвященные доступу к данным, и определим согласованный макет страницы для использования во всех руководствах.

Счастливого программирования!

Об авторе

Скотт Митчелл( Scott Mitchell), автор семи книг ASP/ASP.NET и основатель 4GuysFromRolla.com, работает с веб-технологиями Майкрософт с 1998 года. Скотт работает независимым консультантом, тренером и писателем. Его последняя книга Sams Teach Yourself ASP.NET 2.0 в 24 часах. Он может быть доступен в mitchell@4GuysFromRolla.com. или через его блог, который можно найти по адресу http://ScottOnWriting.NET.

Особая благодарность

Эта серия учебников была рассмотрена многими полезными рецензентами. Ведущим рецензентом этого руководства были Лиз Шулок, Деннис Паттерсон, Карлос Сантос и Хилтон Гисенов. Хотите просмотреть предстоящие статьи MSDN? Если да, опустите мне строку на mitchell@4GuysFromRolla.com.