Compartir a través de


Crear una capa de lógica empresarial (VB)

de Scott Mitchell

Descargar PDF

En este tutorial veremos cómo centralizar las reglas de negocio en una capa de lógica de negocios (BLL) que actúa como intermediario para el intercambio de datos entre la capa de presentación y la DAL.

Introducción

La capa de acceso a datos (DAL) creada en el primer tutorial separa limpiamente la lógica de acceso a datos de la lógica de presentación. Sin embargo, aunque DAL separa de manera limpia los detalles de acceso a los datos de la capa de presentación, no aplica ninguna reglas de negocio que puedan ser aplicadas. Por ejemplo, para nuestra aplicación, es posible que deseemos no permitir que los CategoryID campos o SupplierID de la Products tabla se modifiquen cuando el Discontinued campo está establecido en 1, o es posible que deseemos aplicar reglas de antigüedad, prohibiendo situaciones en las que un empleado es administrado por alguien que se contrató después de ellos. Otro escenario común es la autorización, quizás solo los usuarios de un rol determinado pueden eliminar productos o cambiar el UnitPrice valor.

En este tutorial veremos cómo centralizar estas reglas de negocio en una capa de lógica de negocios (BLL) que actúa como intermediario para el intercambio de datos entre la capa de presentación y la DAL. En una aplicación real, el BLL debe implementarse como un proyecto de biblioteca de clases independiente; Sin embargo, para estos tutoriales implementaremos el BLL como una serie de clases en nuestra App_Code carpeta para simplificar la estructura del proyecto. En la figura 1 se muestran las relaciones arquitectónicas entre la capa de presentación, BLL y DAL.

BLL separa la capa de presentación de la capa de acceso a datos e impone reglas de negocio.

Figura 1: BLL separa la capa de presentación de la capa de acceso a datos e impone reglas de negocios

En lugar de crear clases independientes para implementar nuestra lógica de negocios, podríamos colocar esta lógica directamente en el Conjunto de datos con tipo con clases parciales. Para obtener un ejemplo de creación y extensión de un conjunto de datos con tipo, consulte el primer tutorial.

Paso 1: Crear las clases BLL

Nuestro BLL se compondrá de cuatro clases, una para cada TableAdapter en la DAL; cada clase BLL tendrá métodos para recuperar, insertar, actualizar y eliminar de los respectivos TableAdapter en la DAL, aplicando las reglas de negocio adecuadas.

Para separar de forma más limpia las clases relacionadas con DAL y BLL, vamos a crear dos subcarpetas en la App_Code carpeta DAL y BLL. Solo tiene que hacer clic derecho en la App_Code carpeta en el Explorador de soluciones y elegir Nueva carpeta. Después de crear estas dos carpetas, mueva el Typed DataSet creado en el primer tutorial a la DAL subcarpeta.

A continuación, cree los cuatro archivos de clase BLL en la BLL subcarpeta. Para ello, haga clic con el botón derecho en la BLL subcarpeta, elija Agregar un nuevo elemento y elija la plantilla Clase. Asigne un nombre a las cuatro clases ProductsBLL, CategoriesBLL, SuppliersBLLy EmployeesBLL.

Agregar cuatro clases nuevas a la carpeta App_Code

Figura 2: Agregar cuatro clases nuevas a la App_Code carpeta

A continuación, vamos a agregar métodos a cada una de las clases para simplemente envolver los métodos definidos para los TableAdapters del primer tutorial. Por ahora, estos métodos simplemente llamarán directamente a la DAL; Volveremos más adelante para agregar cualquier lógica de negocios necesaria.

Nota:

Si usa Visual Studio Standard Edition o superior (es decir, no usa Visual Web Developer), opcionalmente puede diseñar las clases visualmente mediante el Diseñador de clases. Consulte el blog del Diseñador de clases para obtener más información sobre esta nueva característica en Visual Studio.

Para la ProductsBLL clase es necesario agregar un total de siete métodos:

  • GetProducts() devuelve todos los productos
  • GetProductByProductID(productID) devuelve el producto con el identificador de producto especificado.
  • GetProductsByCategoryID(categoryID) devuelve todos los productos de la categoría especificada.
  • GetProductsBySupplier(supplierID) devuelve todos los productos del proveedor especificado.
  • AddProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued) inserta un nuevo producto en la base de datos con los valores pasados; devuelve el ProductID valor del registro recién insertado.
  • UpdateProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued, productID)actualiza un producto existente en la base de datos mediante los valores pasados; devuelve True si se actualizó exactamente una fila; de lo contrario, False
  • DeleteProduct(productID) elimina el producto especificado de la base de datos.

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

Los métodos que simplemente devuelven datos GetProducts, GetProductByProductID, GetProductsByCategoryIDy GetProductBySuppliersID son bastante sencillos, ya que simplemente llaman a la DAL. Aunque en algunos escenarios puede haber reglas de negocios que deba implementarse en este nivel (por ejemplo, reglas de autorización basadas en el usuario que ha iniciado sesión actualmente o el rol al que pertenece el usuario), simplemente dejaremos estos métodos as-is. Para estos métodos, el BLL sirve simplemente como un proxy a través del cual la capa de presentación accede a los datos subyacentes desde la capa de acceso a datos.

Los AddProduct métodos y UpdateProduct toman como parámetros los valores de los distintos campos de producto y agregan un nuevo producto o actualizan uno existente, respectivamente. Dado que muchas de las columnas de la Product tabla pueden aceptar NULL valores (CategoryID, SupplierID, y UnitPrice, para nombrar algunos), esos parámetros de entrada para AddProduct y UpdateProduct que se asignan a estas columnas usan tipos que aceptan valores NULL. Los tipos que aceptan valores NULL son nuevos en .NET 2.0 y proporcionan una técnica para indicar si un tipo de valor debe, en su lugar, ser Nothing. Consulte la entrada de blog de Paul VickLa verdad sobre los tipos que admiten valores NULL y VB y la documentación técnica de la estructura Nullable para obtener más información.

Los tres métodos devuelven un valor booleano que indica si se insertó, actualizó o eliminó una fila, ya que es posible que la operación no produzca una fila afectada. Por ejemplo, si el desarrollador de páginas llama a DeleteProduct pasando un ProductID para un producto no existente, la DELETE instrucción emitida a la base de datos no tendrá ningún efecto y, por tanto, el método DeleteProduct devolverá False.

Tenga en cuenta que al agregar un nuevo producto o actualizar uno existente, tomamos los valores de campo del producto nuevo o modificado como una lista de escalares en lugar de aceptar una ProductsRow instancia. Este enfoque se eligió porque la ProductsRow clase deriva de la clase ADO.NET DataRow , que no tiene un constructor sin parámetros predeterminado. Para crear una nueva ProductsRow instancia, primero debemos crear una ProductsDataTable instancia y, a continuación, invocar su NewProductRow() método (lo que hacemos en AddProduct). Este problema se manifiesta cuando insertamos y actualizamos productos mediante ObjectDataSource. En resumen, ObjectDataSource intentará crear una instancia de los parámetros de entrada. Si el método BLL espera una ProductsRow instancia, ObjectDataSource intentará crear una, pero producirá un error debido a la falta de un constructor sin parámetros predeterminado. Para obtener más información sobre este problema, consulte las siguientes dos publicaciones de foros de ASP.NET: Actualizar ObjectDataSources con Strongly-Typed DataSets y Problema con ObjectDataSource y Strongly-Typed DataSet.

A continuación, en ambas AddProduct y UpdateProduct, el código crea una instancia de ProductsRow y la llena con los valores recién pasados. Al asignar valores a DataColumns de un dataRow se pueden producir varias comprobaciones de validación de nivel de campo. Por lo tanto, volver a colocar manualmente los valores pasados en dataRow ayuda a garantizar la validez de los datos que se pasan al método BLL. Desafortunadamente, las clases DataRow fuertemente tipadas generadas por Visual Studio no usan tipos que aceptan valores NULL. En su lugar, para indicar que un objeto DataColumn determinado de un DataRow debe corresponder a un valor de NULL base de datos, debemos usar el SetColumnNameNull() método.

En UpdateProduct primero cargamos el producto que se va a actualizar con GetProductByProductID(productID). Aunque esto puede parecer un viaje innecesario a la base de datos, este viaje adicional valdrá la pena en los tutoriales futuros que exploran la simultaneidad optimista. La simultaneidad optimista es una técnica para asegurarse de que dos usuarios que trabajan simultáneamente en los mismos datos no sobrescriben accidentalmente los cambios de otro. Agarrar todo el registro también facilita la creación de métodos de actualización en el BLL que solo modifican un subconjunto de las columnas de DataRow. Cuando exploremos la SuppliersBLL clase veremos un ejemplo de este tipo.

Por último, tenga en cuenta que la ProductsBLL clase tiene aplicado el atributo DataObject (la [System.ComponentModel.DataObject] sintaxis justo antes de la instrucción de clase cerca de la parte superior del archivo) y los métodos tienen atributos DataObjectMethodAttribute. El DataObject atributo marca la clase como un objeto adecuado para enlazar a un control ObjectDataSource, mientras que indica DataObjectMethodAttribute el propósito del método. Como veremos en los tutoriales futuros, ASP.NET ObjectDataSource de ASP.NET 2.0 facilita el acceso declarativo a los datos desde una clase. Para ayudar a filtrar la lista de posibles clases a las que enlazar en el asistente objectDataSource, de forma predeterminada solo se muestran esas clases marcadas como DataObjects en la lista desplegable del asistente. La ProductsBLL clase funcionará igual de bien sin estos atributos, pero agregarlos facilita el trabajo en el asistente de ObjectDataSource.

Adición de otras clases

Una vez que la ProductsBLL clase esté completa, aún necesitamos añadir las clases para trabajar con categorías, proveedores y empleados. Dedique un momento a crear las siguientes clases y métodos mediante los conceptos del ejemplo anterior:

  • CategoriesBLL.cs

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

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

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

El método que merece la pena tener en cuenta es el método SuppliersBLL de la clase UpdateSupplierAddress. Este método proporciona una interfaz para actualizar solo la información de dirección del proveedor. Internamente, este método lee el objeto SupplierDataRow especificado por supplierID (mediante GetSupplierBySupplierID), establece sus propiedades relacionadas con la dirección y, a continuación, llama al método SupplierDataTable del Update. El método UpdateSupplierAddress sigue:

<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

Consulte la descarga de este artículo para obtener mi implementación completa de las clases BLL.

Paso 2: Obtener acceso a los conjuntos de datos con tipo a través de las clases BLL

En el primer tutorial vimos ejemplos de cómo trabajar directamente con el DataSet tipado de forma programática, pero con la incorporación de nuestras clases BLL, el nivel de presentación debería interactuar principalmente con el BLL. En el AllProducts.aspx ejemplo del primer tutorial, ProductsTableAdapter se usó para enlazar la lista de productos a gridView, como se muestra en el código siguiente:

Dim productsAdapter As New ProductsTableAdapter()
GridView1.DataSource = productsAdapter.GetProducts()
GridView1.DataBind()

Para usar las nuevas clases BLL, todo lo que debe cambiarse es la primera línea de código simplemente reemplaza el ProductsTableAdapter objeto por un ProductBLL objeto :

Dim productLogic As New ProductsBLL()
GridView1.DataSource = productLogic.GetProducts()
GridView1.DataBind()

También se puede acceder a las clases BLL de manera declarativa (igual que al DataSet tipado) mediante ObjectDataSource. Analizaremos objectDataSource con más detalle en los tutoriales siguientes.

La lista de productos se muestra en un GridView

Figura 3: La lista de productos se muestra en gridView (haga clic para ver la imagen de tamaño completo)

Paso 3: Agregar la Field-Level Validación a las clases DataRow

La validación de nivel de campo es una comprobación que se refiere a los valores de las propiedades de los objetos de negocio al insertar o actualizar. Algunas reglas de validación de nivel de campo para los productos incluyen:

  • El ProductName campo debe tener 40 caracteres o menos de longitud.
  • El QuantityPerUnit campo debe tener 20 caracteres o menos de longitud.
  • Los ProductIDcampos , ProductNamey Discontinued son obligatorios, pero todos los demás campos son opcionales.
  • Los UnitPrice, UnitsInStock, UnitsOnOrder y ReorderLevel deben ser mayores o iguales que cero.

Estas reglas pueden expresarse y deben expresarse en el nivel de base de datos. Los tipos de datos de esas columnas capturan el límite de caracteres de las columnas ProductName y QuantityPerUnit en la tabla Products (nvarchar(40) y nvarchar(20), respectivamente). Si los campos son obligatorios u opcionales, se determina por si la columna de la tabla de la base de datos permite NULL s. Existen cuatro restricciones de verificación que garantizan que solo los valores mayores o iguales a cero pueden entrar en las , UnitPrice, UnitsInStock o UnitsOnOrder.

Además de aplicar estas reglas en la base de datos, también deben aplicarse en el nivel DataSet. De hecho, la longitud del campo y si un valor es obligatorio u opcional ya se capturan para el conjunto de DataColumns de cada DataTable. Para ver la validación de nivel de campo existente proporcionada automáticamente, vaya al Diseñador de conjuntos de datos, seleccione un campo de una de las Tablas de datos y, a continuación, vaya a la ventana Propiedades. Como se muestra en la Figura 4, la QuantityPerUnit DataColumn en el ProductsDataTable tiene una longitud máxima de 20 caracteres y permite valores NULL. Si intentamos establecer la propiedad ProductsDataRow de QuantityPerUnit a un valor de cadena de más de 20 caracteres, se producirá una excepción.

DataColumn proporciona validación básica de Field-Level

Figura 4: DataColumn proporciona validación básica de Field-Level (haga clic para ver la imagen de tamaño completo)

Desafortunadamente, no se pueden especificar comprobaciones de límites, como el UnitPrice valor debe ser mayor o igual que cero, a través de la ventana Propiedades. Para proporcionar este tipo de validación de nivel de campo, es necesario crear un controlador de eventos para el evento ColumnChanging de DataTable. Como se mencionó en el tutorial anterior, los objetos DataSet, DataTables y DataRow creados por typed DataSet se pueden ampliar mediante el uso de clases parciales. Con esta técnica se puede crear un ColumnChanging controlador de eventos para la ProductsDataTable clase . Empiece por crear una clase en la App_Code carpeta denominada ProductsDataTable.ColumnChanging.vb.

Agregar una nueva clase a la carpeta App_Code

Figura 5: Agregar una nueva clase a la carpeta (App_Code imagen de tamaño completo)

A continuación, cree un controlador de eventos para el ColumnChanging evento que garantice que los UnitPricevalores de columna , UnitsInStock, UnitsOnOrdery ReorderLevel (si no NULL) sean mayores o iguales que cero. Si alguna de estas columnas está fuera del intervalo, lanza una excepción 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

Paso 4: Agregar reglas de negocio personalizadas a las clases de BLL

Además de la validación de nivel de campo, puede haber reglas de negocio personalizadas de alto nivel que impliquen diferentes entidades o conceptos que no se puedan expresar en el nivel de columna única, como:

  • Si un producto se descontinúa, su UnitPrice no se puede actualizar.
  • El país de residencia de un empleado debe ser el mismo que el país de residencia de su gerente.
  • No se puede interrumpir un producto si es el único producto proporcionado por el proveedor

Las clases BLL deben contener comprobaciones para garantizar el cumplimiento de las reglas de negocios de la aplicación. Estas comprobaciones se pueden agregar directamente a los métodos a los que se aplican.

Imagine que nuestras reglas de negocio dictan que un producto no se pudo marcar descontinuado si era el único producto de un proveedor determinado. Es decir, si el producto X era el único producto que compramos del proveedor Y, no podíamos marcar X como descontinuado; si, sin embargo, el proveedor Y nos proporcionó tres productos, A, B y C, entonces podríamos marcar cualquiera y todos ellos como discontinuos. Una regla de negocios extraña, pero las reglas de negocios y el sentido común no siempre están alineadas.

Para aplicar esta regla de negocio en el método UpdateProducts, comenzaríamos comprobando si Discontinued se estableció como True y, si es así, llamaríamos a GetProductsBySupplierID para determinar cuántos productos compramos al proveedor de este producto. Si solo se compra un producto de este proveedor, lanzamos una ApplicationExceptionexcepción.

<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

Responder a errores de validación en el nivel de presentación

Al llamar al BLL desde el nivel de presentación, podemos decidir si controlamos las excepciones que podrían generarse o dejamos que se propaguen hasta ASP.NET, lo que provocará el evento HttpApplicationError. Para manejar una excepción mientras se trabaja programáticamente con BLL, podemos utilizar un bloque Try...Catch, como muestra el siguiente ejemplo.

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

Como veremos en tutoriales futuros, el control de excepciones que se propagan desde el BLL cuando se usa un control web de datos para insertar, actualizar o eliminar datos se puede controlar directamente en un controlador de eventos, en lugar de tener que encapsular código en Try...Catch bloques.

Resumen

Una aplicación bien diseñada se crea en capas distintas, cada una de las cuales encapsula un rol determinado. En el primer tutorial de esta serie de artículos creamos una capa de acceso a datos mediante conjuntos de datos con tipo; en este tutorial creamos una capa de lógica de negocios como una serie de clases en la carpeta App_Code de la aplicación que llama a nuestra DAL. BLL implementa la lógica de nivel de campo y de nivel empresarial para nuestra aplicación. Además de crear un BLL independiente, como hicimos en este tutorial, otra opción es ampliar los métodos de TableAdapters mediante el uso de clases parciales. Sin embargo, el uso de esta técnica no nos permite invalidar los métodos existentes ni separar nuestra DAL y nuestra BLL tan limpiamente como el enfoque que hemos tomado en este artículo.

Con DAL y BLL completados, estamos listos para empezar con nuestra capa de presentación. En el siguiente tutorial , tomaremos un breve desvío de los temas de acceso a datos y definiremos un diseño de página coherente para usarlo en todos los tutoriales.

¡Feliz programación!

Acerca del autor

Scott Mitchell, autor de siete libros de ASP/ASP.NET y fundador de 4GuysFromRolla.com, ha estado trabajando con tecnologías web de Microsoft desde 1998. Scott trabaja como consultor independiente, entrenador y escritor. Su último libro es Sams Teach Yourself ASP.NET 2.0 en 24 horas. Se puede contactar con él en mitchell@4GuysFromRolla.com.

Agradecimientos especiales a

Esta serie de tutoriales contó con la revisión de muchos revisores que fueron de gran ayuda. Los revisores principales de este tutorial fueron Liz Shulok, Dennis Patterson, Carlos Santos y Hilton Giesenow. ¿Le interesa revisar mis próximos artículos de MSDN? Si es así, mándame un mensaje a mitchell@4GuysFromRolla.com.