Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
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.
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
, SuppliersBLL
y EmployeesBLL
.
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 elProductID
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; devuelveTrue
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
, GetProductsByCategoryID
y 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.
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
ProductID
campos ,ProductName
yDiscontinued
son obligatorios, pero todos los demás campos son opcionales. - Los
UnitPrice
,UnitsInStock
,UnitsOnOrder
yReorderLevel
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.
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
.
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 UnitPrice
valores de columna , UnitsInStock
, UnitsOnOrder
y 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 ApplicationException
excepció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 HttpApplication
Error
. 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.