Compartilhar via


Criação de uma camada de lógica de negócios (VB)

por Scott Mitchell

Baixar PDF

Neste tutorial, veremos como centralizar suas regras de negócios em uma BLL (Camada lógica de negócios) que serve como um intermediário para a troca de dados entre a camada de apresentação e o DAL.

Introdução

A DAL (Camada de Acesso a Dados) criada no primeiro tutorial separa de forma limpa a lógica de acesso a dados da lógica de apresentação. No entanto, embora o DAL separe de forma limpa os detalhes de acesso a dados da camada de apresentação, ele não impõe nenhuma regra de negócios que possa ser aplicada. Por exemplo, para nosso aplicativo, talvez queiramos não permitir que os CategoryID campos ou SupplierID campos da Products tabela sejam modificados quando o Discontinued campo for definido como 1, ou talvez queiramos impor regras de antiguidade, proibindo situações em que um funcionário é gerenciado por alguém que foi contratado depois deles. Outro cenário comum é a autorização talvez apenas os usuários em uma função específica possam excluir produtos ou alterar o UnitPrice valor.

Neste tutorial, veremos como centralizar essas regras de negócios em uma BLL (Camada lógica de negócios) que serve como um intermediário para troca de dados entre a camada de apresentação e o DAL. Em um aplicativo do mundo real, a BLL deve ser implementada como um projeto separado da Biblioteca de Classes; no entanto, para esses tutoriais, implementaremos a BLL como uma série de classes em nossa App_Code pasta, a fim de simplificar a estrutura do projeto. A Figura 1 ilustra as relações arquitetônicas entre a camada de apresentação, a BLL e a DAL.

A BLL separa a camada de apresentação da camada de acesso a dados e impõe regras de negócios

Figura 1: A BLL separa a camada de apresentação da camada de acesso a dados e impõe regras de negócios

Em vez de criar classes separadas para implementar nossa lógica de negócios, poderíamos, como alternativa, colocar essa lógica diretamente no Conjunto de Dados Tipado com classes parciais. Para obter um exemplo de criação e extensão de um Conjunto de Dados Tipado, consulte o primeiro tutorial.

Etapa 1: Criando as Classes BLL

Nossa BLL será composta por quatro classes, uma para cada TableAdapter no DAL; cada uma dessas classes BLL terá métodos para recuperar, inserir, atualizar e excluir do respectivo TableAdapter no DAL, aplicando as regras de negócios apropriadas.

Para separar de forma mais limpa as classes relacionadas a DAL e BLL, vamos criar duas subpastas na App_Code pasta DAL e BLL. Basta clicar com o botão direito do App_Code mouse na pasta no Gerenciador de Soluções e escolher Nova Pasta. Depois de criar essas duas pastas, mova o Conjunto de Dados Digitado criado no primeiro tutorial para a DAL subpasta.

Em seguida, crie os quatro arquivos de classe BLL na BLL subpasta. Para fazer isso, clique com o botão direito do BLL mouse na subpasta, escolha Adicionar um Novo Item e escolha o modelo de Classe. Nomeie as quatro classes ProductsBLL, CategoriesBLL, SuppliersBLL e EmployeesBLL.

Adicionar quatro novas classes à pasta App_Code

Figura 2: Adicionar quatro novas classes à App_Code pasta

Em seguida, vamos adicionar métodos a cada uma das classes para simplesmente encapsular os métodos definidos para o TableAdapters do primeiro tutorial. Por enquanto, esses métodos apenas chamarão diretamente para o DAL; retornaremos mais tarde para adicionar qualquer lógica de negócios necessária.

Observação

Se você estiver usando o Visual Studio Standard Edition ou superior (ou seja, não está usando o Visual Web Developer), opcionalmente, poderá projetar suas classes visualmente usando o Designer de Classe. Consulte o Blog do Designer de Classe para obter mais informações sobre esse novo recurso no Visual Studio.

Para a ProductsBLL classe, precisamos adicionar um total de sete métodos:

  • GetProducts() retorna todos os produtos
  • GetProductByProductID(productID) retorna o produto com a ID do produto especificada
  • GetProductsByCategoryID(categoryID) retorna todos os produtos da categoria especificada
  • GetProductsBySupplier(supplierID) retorna todos os produtos do fornecedor especificado
  • AddProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued) insere um novo produto no banco de dados usando os valores passados; retorna o ProductID valor do registro recém-inserido
  • UpdateProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued, productID) atualiza um produto existente no banco de dados usando os valores passados; retornará True se precisamente uma linha foi atualizada, False caso contrário
  • DeleteProduct(productID) exclui o produto especificado do banco de dados

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

Os métodos que simplesmente retornam dados GetProducts, GetProductByProductID, GetProductsByCategoryID e GetProductBySuppliersID são bastante diretos, pois apenas chamam o DAL. Embora em alguns cenários possa haver regras de negócios que precisam ser implementadas nesse nível (como regras de autorização com base no usuário conectado no momento ou na função à qual o usuário pertence), simplesmente deixaremos esses métodos as-is. Para esses métodos, a BLL serve apenas como um proxy por meio do qual a camada de apresentação acessa os dados subjacentes da Camada de Acesso a Dados.

Os métodos AddProduct e UpdateProduct aceitam como parâmetros os valores dos vários campos de produto e adicionam um novo produto ou atualizam um existente, respectivamente. Como muitas das Product colunas da tabela podem aceitar NULL valores (CategoryID, SupplierID e UnitPrice, para citar alguns), os parâmetros de entrada para AddProduct e UpdateProduct que mapeiam para essas colunas usam tipos anuláveis. Tipos anuláveis são novos no .NET 2.0 e fornecem uma técnica para indicar se um tipo de valor deve, em vez disso, ser Nothing. Consulte o entrada do blog de Paul VickThe Truth About Nullable Types and VB e a documentação técnica da estrutura Nullable para obter mais informações.

Todos os três métodos retornam um valor booliano indicando se uma linha foi inserida, atualizada ou excluída, pois a operação pode não resultar em uma linha afetada. Por exemplo, se o desenvolvedor de páginas chamar DeleteProduct passando um ProductID para um produto inexistente, a instrução DELETE emitida para o banco de dados não terá efeito nenhum e, portanto, o método DeleteProduct retornará False.

Observe que, ao adicionar um novo produto ou atualizar um existente, usamos os valores de campo do produto novo ou modificado como uma lista de escalares em vez de aceitar uma ProductsRow instância. Essa abordagem foi escolhida porque a ProductsRow classe deriva da classe ADO.NET DataRow , que não tem um construtor sem parâmetros padrão. Para criar uma nova ProductsRow instância, primeiro devemos criar uma ProductsDataTable instância e, em seguida, invocar seu NewProductRow() método (o que fazemos em AddProduct). Essa deficiência se manifesta quando recorremos à inserção e atualização de produtos ao usar o ObjectDataSource. Em suma, o ObjectDataSource tentará criar uma instância dos parâmetros de entrada. Se o método BLL espera uma ProductsRow instância, o ObjectDataSource tentará criar uma, mas falhará devido à falta de um construtor sem parâmetros padrão. Para obter mais informações sobre esse problema, consulte as duas postagens de fóruns de ASP.NET a seguir: Atualizando ObjectDataSources com Strongly-Typed DataSets e Problema com ObjectDataSource e Strongly-Typed DataSet.

Em seguida, em ambos AddProduct e UpdateProduct, o código cria uma ProductsRow instância e a preenche com os valores passados. Ao atribuir valores a DataColumns de um DataRow, várias verificações de validação de nível de campo podem ocorrer. Portanto, colocar manualmente os valores passados de volta em um DataRow ajuda a garantir a validade dos dados que estão sendo passados para o método BLL. Infelizmente, as classes DataRow fortemente tipadas geradas pelo Visual Studio não utilizam tipos anuláveis. Em vez disso, para indicar que um DataColumn específico em um DataRow deve corresponder a um NULL valor de banco de dados, devemos usar o SetColumnNameNull() método.

Em UpdateProduct primeiro carregamos o produto que queremos atualizar usando GetProductByProductID(productID). Embora isso possa parecer uma viagem desnecessária ao banco de dados, essa viagem extra será útil em tutoriais futuros que exploram a simultaneidade otimista. A simultaneidade otimista é uma técnica para garantir que dois usuários que estão trabalhando simultaneamente nos mesmos dados não substituam acidentalmente as alterações uns dos outros. A captura de todo o registro também facilita a criação de métodos de atualização na BLL que modificam apenas um subconjunto das colunas do DataRow. Quando explorarmos a SuppliersBLL classe, veremos esse exemplo.

Por fim, observe que a ProductsBLL classe tem o atributo DataObject aplicado a ela (a [System.ComponentModel.DataObject] sintaxe logo antes da instrução de classe próxima à parte superior do arquivo) e os métodos têm atributos DataObjectMethodAttribute. O DataObject atributo marca a classe como sendo um objeto adequado para associação a um controle ObjectDataSource, enquanto o indica a DataObjectMethodAttribute finalidade do método. Como veremos em tutoriais futuros, o ObjectDataSource do ASP.NET 2.0 facilita o acesso declarativo a dados de uma classe. Para ajudar a filtrar a lista de classes possíveis a serem associadas no assistente do ObjectDataSource, por padrão, somente as classes marcadas como DataObjects são mostradas na lista suspensa do assistente. A ProductsBLL classe funcionará tão bem sem esses atributos, mas adicioná-los facilita o trabalho no assistente do ObjectDataSource.

Adicionando as outras classes

Com a ProductsBLL classe concluída, ainda precisamos adicionar as classes para trabalhar com categorias, fornecedores e funcionários. Reserve um momento para criar as seguintes classes e métodos usando os conceitos do exemplo acima:

  • CategoriesBLL.cs

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

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

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

O único método que vale a pena observar é o método SuppliersBLL da classe UpdateSupplierAddress. Esse método fornece uma interface para atualizar apenas as informações de endereço do fornecedor. Internamente, este método lê o objeto SupplierDataRow para o especificado supplierID usando GetSupplierBySupplierID, define suas propriedades relacionadas ao endereço e, em seguida, chama o método SupplierDataTable do Update. O UpdateSupplierAddress método segue:

<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 o download deste artigo para minha implementação completa das classes BLL.

Etapa 2: Acessar os DataSets Tipados através das classes BLL

No primeiro tutorial, vimos exemplos de como trabalhar diretamente com o Conjunto de Dados Digitado programaticamente, mas com a adição de nossas classes BLL, a camada de apresentação deve funcionar em relação à BLL. AllProducts.aspx No exemplo do primeiro tutorial, ele ProductsTableAdapter foi usado para associar a lista de produtos a um GridView, conforme mostrado no código a seguir:

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

Para usar as novas classes BLL, tudo o que precisa ser alterado é que a primeira linha de código simplesmente substitua o ProductsTableAdapter objeto por um ProductBLL objeto:

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

As classes BLL também podem ser acessadas declarativamente (assim como o Conjunto de Dados Digitado) usando o ObjectDataSource. Discutiremos o ObjectDataSource com mais detalhes nos tutoriais a seguir.

A lista de produtos é exibida em um GridView

Figura 3: A lista de produtos é exibida em um GridView (clique para exibir a imagem em tamanho real)

Etapa 3: Adicionando Validação Field-Level às classes DataRow

A validação em nível de campo é uma verificação que se refere aos valores de propriedade dos objetos de negócios ao inserir ou atualizar. Algumas regras de validação de nível de campo para produtos incluem:

  • O ProductName campo deve ter 40 caracteres ou menos de comprimento
  • O QuantityPerUnit campo deve ter 20 caracteres ou menos de comprimento
  • Os ProductIDcampos , ProductNamee Discontinued são necessários, mas todos os outros campos são opcionais
  • Os UnitPricecampos , UnitsInStock, UnitsOnOrdere ReorderLevel devem ser maiores ou iguais a zero

Essas regras podem e devem ser expressas no nível do banco de dados. O limite de caracteres nos campos ProductName e QuantityPerUnit é capturado pelos tipos de dados dessas colunas na tabela Products (nvarchar(40) e nvarchar(20), respectivamente). Se os campos são necessários e opcionais são expressos se a coluna da tabela de banco de NULL dados permite s. Existem quatro restrições de verificação que garantem que somente valores maiores ou iguais a zero possam entrar nas colunas UnitPrice, UnitsInStock, UnitsOnOrder e ReorderLevel.

Além de impor essas regras no banco de dados, elas também devem ser impostas no nível do DataSet. Na verdade, o comprimento do campo e se um valor é necessário ou opcional já são capturados para cada conjunto de DataColumns de cada DataTable. Para ver a validação de nível de campo existente fornecida automaticamente, vá para o Designer de Conjunto de Dados, selecione um campo de uma das DataTables e vá para a janela Propriedades. Como mostra a Figura 4, o QuantityPerUnit DataColumn no ProductsDataTable tem um comprimento máximo de 20 caracteres e permite NULL valores. Se tentarmos definir a propriedade ProductsDataRow de QuantityPerUnit para um valor de cadeia de caracteres com mais de 20 caracteres, uma ArgumentException será lançada.

O DataColumn fornece validação básica de Field-Level

Figura 4: O DataColumn fornece validação básica de Field-Level (clique para exibir a imagem em tamanho real)

Infelizmente, não é possível especificar verificações de limites, como o valor UnitPrice deve ser maior ou igual a zero, por meio da janela de propriedades. Para fornecer esse tipo de validação em nível de campo, precisamos criar um manipulador de eventos para o evento ColumnChanging do DataTable. Conforme mencionado no tutorial anterior, os objetos DataSet, DataTables e DataRow criados pelo Conjunto de Dados Tipado podem ser estendidos por meio do uso de classes parciais. Usando essa técnica, podemos criar um ColumnChanging manipulador de eventos para a ProductsDataTable classe. Comece criando uma classe na App_Code pasta chamada ProductsDataTable.ColumnChanging.vb.

Adicionar uma nova classe à pasta App_Code

Figura 5: Adicionar uma nova classe à App_Code pasta (clique para exibir a imagem em tamanho real)

Em seguida, crie um manipulador de eventos para o evento ColumnChanging que garanta que os valores das colunas UnitPrice, UnitsInStock, UnitsOnOrder e ReorderLevel (se não NULL) sejam maiores ou iguais a zero. Se qualquer coluna desse tipo estiver fora do intervalo, lance um 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

Etapa 4: Adicionando regras de negócios personalizadas às classes da BLL

Além da validação em nível de campo, pode haver regras de negócios personalizadas de alto nível que envolvem diferentes entidades ou conceitos não expressíveis no nível de coluna única, como:

  • Se um produto for descontinuado, ele UnitPrice não poderá ser atualizado
  • O país de residência de um funcionário deve ser o mesmo que o país de residência de seu gerente
  • Um produto não poderá ser descontinuado se for o único produto fornecido pelo fornecedor

As classes BLL devem conter verificações para garantir a adesão às regras de negócios do aplicativo. Essas verificações podem ser adicionadas diretamente aos métodos aos quais se aplicam.

Imagine que nossas regras comerciais determinam que um produto não poderia ser marcado como descontinuado se fosse o único produto de um determinado fornecedor. Ou seja, se o produto X foi o único produto que compramos do fornecedor Y, não poderíamos marcar X como descontinuado; se, no entanto, o fornecedor Y nos forneceu três produtos, A, B e C, então poderíamos marcar qualquer um e todos eles como descontinuados. Uma regra de negócios estranha, mas as regras de negócios e o bom senso nem sempre estão alinhadas!

Para impor essa regra de negócios no UpdateProducts método, começaríamos verificando se Discontinued estava definido True e, nesse caso, chamaríamos GetProductsBySupplierID para determinar quantos produtos compramos do fornecedor deste produto. Se apenas um produto for comprado deste fornecedor, lançaremos um 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

Respondendo a erros de validação na camada de apresentação

Ao chamar a BLL da camada de apresentação, podemos decidir se tentamos lidar com quaisquer exceções que possam ser geradas ou deixá-las subir até o ASP.NET (o que acionará o evento do HttpApplicationError). Para lidar com uma exceção ao trabalhar com a BLL programaticamente, podemos usar um Try...Catch, como mostra o exemplo a seguir.

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 em tutoriais futuros, o tratamento de exceções que geram bolhas da BLL ao usar um controle da Web de dados para inserir, atualizar ou excluir dados pode ser tratado diretamente em um manipulador de eventos em vez de ter que encapsular código em Try...Catch blocos.

Resumo

Um aplicativo bem arquiteto é criado em camadas distintas, cada uma das quais encapsula uma função específica. No primeiro tutorial desta série de artigos, criamos uma Camada de Acesso a Dados usando Conjuntos de Dados Tipados; neste tutorial, criamos uma Camada Lógica de Negócios como uma série de classes na pasta App_Code do nosso aplicativo que fazem chamadas ao nosso DAL. A BLL implementa a lógica de nível de campo e de negócios para nosso aplicativo. Além de criar uma BLL separada, como fizemos neste tutorial, outra opção é estender os métodos do TableAdapters por meio do uso de classes parciais. No entanto, o uso dessa técnica não nos permite substituir os métodos existentes nem separa nosso DAL e nossa BLL tão claramente quanto a abordagem que adotamos neste artigo.

Com a DAL e a BLL concluídas, estamos prontos para começar na nossa camada de apresentação. No próximo tutorial , faremos um breve desvio dos tópicos de acesso a dados e definiremos um layout de página consistente para uso em todos os tutoriais.

Divirta-se programando!

Sobre o autor

Scott Mitchell, autor de sete livros asp/ASP.NET e fundador da 4GuysFromRolla.com, trabalha com tecnologias da Microsoft Web desde 1998. Scott trabalha como consultor independente, treinador e escritor. Seu último livro é Sams Teach Yourself ASP.NET 2.0 em 24 Horas. Ele pode ser alcançado em mitchell@4GuysFromRolla.com.

Agradecimentos Especiais a

Esta série de tutoriais foi revisada por muitos revisores úteis. Os principais revisores deste tutorial foram Liz Shulok, Dennis Patterson, Carlos Santos e Hilton Giesenow. Interessado em revisar meus próximos artigos do MSDN? Se assim for, deixe-me uma linha em mitchell@4GuysFromRolla.com.