Criação de uma camada de lógica de negócios (C#)
por Scott Mitchell
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 corretamente a lógica de acesso a dados da lógica de apresentação. No entanto, embora o DAL separe corretamente 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
da Products
tabela sejam modificados quando o Discontinued
campo estiver 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 de 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. 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 para 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.
Figura 1: a BLL separa a camada de apresentação da camada de acesso a dados e impõe regras de negócios
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 na DAL, aplicando as regras de negócios apropriadas.
Para separar mais claramente 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 Tipado 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 Classe. Nomeie as quatro classes ProductsBLL
, CategoriesBLL
, SuppliersBLL
e EmployeesBLL
.
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 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 estiver usando o Visual Web Developer), opcionalmente, poderá projetar suas classes visualmente usando a Designer Classe. Consulte o Blog de 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 produtosGetProductByProductID(productID)
retorna o produto com a ID do produto especificadaGetProductsByCategoryID(categoryID)
retorna todos os produtos da categoria especificadaGetProductsBySupplier(supplierID)
retorna todos os produtos do fornecedor especificadoAddProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued)
insere um novo produto no banco de dados usando os valores passados; retorna oProductID
valor do registro recém-inseridoUpdateProduct(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 tiver sido atualizada; caso contrário,false
DeleteProduct(productID)
exclui o produto especificado do banco de dados
ProductsBLL.cs
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using NorthwindTableAdapters;
[System.ComponentModel.DataObject]
public class ProductsBLL
{
private ProductsTableAdapter _productsAdapter = null;
protected ProductsTableAdapter Adapter
{
get {
if (_productsAdapter == null)
_productsAdapter = new ProductsTableAdapter();
return _productsAdapter;
}
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Select, true)]
public Northwind.ProductsDataTable GetProducts()
{
return Adapter.GetProducts();
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Select, false)]
public Northwind.ProductsDataTable GetProductByProductID(int productID)
{
return Adapter.GetProductByProductID(productID);
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Select, false)]
public Northwind.ProductsDataTable GetProductsByCategoryID(int categoryID)
{
return Adapter.GetProductsByCategoryID(categoryID);
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Select, false)]
public Northwind.ProductsDataTable GetProductsBySupplierID(int supplierID)
{
return Adapter.GetProductsBySupplierID(supplierID);
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Insert, true)]
public bool AddProduct(string productName, int? supplierID, int? categoryID,
string quantityPerUnit, decimal? unitPrice, short? unitsInStock,
short? unitsOnOrder, short? reorderLevel, bool discontinued)
{
// Create a new ProductRow instance
Northwind.ProductsDataTable products = new Northwind.ProductsDataTable();
Northwind.ProductsRow product = products.NewProductsRow();
product.ProductName = productName;
if (supplierID == null) product.SetSupplierIDNull();
else product.SupplierID = supplierID.Value;
if (categoryID == null) product.SetCategoryIDNull();
else product.CategoryID = categoryID.Value;
if (quantityPerUnit == null) product.SetQuantityPerUnitNull();
else product.QuantityPerUnit = quantityPerUnit;
if (unitPrice == null) product.SetUnitPriceNull();
else product.UnitPrice = unitPrice.Value;
if (unitsInStock == null) product.SetUnitsInStockNull();
else product.UnitsInStock = unitsInStock.Value;
if (unitsOnOrder == null) product.SetUnitsOnOrderNull();
else product.UnitsOnOrder = unitsOnOrder.Value;
if (reorderLevel == null) product.SetReorderLevelNull();
else product.ReorderLevel = reorderLevel.Value;
product.Discontinued = discontinued;
// Add the new product
products.AddProductsRow(product);
int rowsAffected = Adapter.Update(products);
// Return true if precisely one row was inserted,
// otherwise false
return rowsAffected == 1;
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Update, true)]
public bool UpdateProduct(string productName, int? supplierID, int? categoryID,
string quantityPerUnit, decimal? unitPrice, short? unitsInStock,
short? unitsOnOrder, short? reorderLevel, bool discontinued, int productID)
{
Northwind.ProductsDataTable products = Adapter.GetProductByProductID(productID);
if (products.Count == 0)
// no matching record found, return false
return false;
Northwind.ProductsRow product = products[0];
product.ProductName = productName;
if (supplierID == null) product.SetSupplierIDNull();
else product.SupplierID = supplierID.Value;
if (categoryID == null) product.SetCategoryIDNull();
else product.CategoryID = categoryID.Value;
if (quantityPerUnit == null) product.SetQuantityPerUnitNull();
else product.QuantityPerUnit = quantityPerUnit;
if (unitPrice == null) product.SetUnitPriceNull();
else product.UnitPrice = unitPrice.Value;
if (unitsInStock == null) product.SetUnitsInStockNull();
else product.UnitsInStock = unitsInStock.Value;
if (unitsOnOrder == null) product.SetUnitsOnOrderNull();
else product.UnitsOnOrder = unitsOnOrder.Value;
if (reorderLevel == null) product.SetReorderLevelNull();
else product.ReorderLevel = reorderLevel.Value;
product.Discontinued = discontinued;
// Update the product record
int rowsAffected = Adapter.Update(product);
// Return true if precisely one row was updated,
// otherwise false
return rowsAffected == 1;
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Delete, true)]
public bool DeleteProduct(int productID)
{
int rowsAffected = Adapter.Delete(productID);
// Return true if precisely one row was deleted,
// otherwise false
return rowsAffected == 1;
}
}
Os métodos que simplesmente retornam dados GetProducts
, GetProductByProductID
, GetProductsByCategoryID
e GetProductBySuppliersID
são bastante simples, pois simplesmente chamam para 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 no estado em que se encontra. 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 AddProduct
métodos e UpdateProduct
assumem como parâmetros os valores para os 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), esses parâmetros de entrada para AddProduct
e UpdateProduct
que são mapeados 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 null
. No C#, você pode sinalizar um tipo de valor como um tipo que permite valor nulo adicionando ?
após o tipo (como int? x;
). Consulte a seção Tipos anuláveis no Guia de Programação em C# para obter mais informações.
Todos os três métodos retornam um valor booliano que indica 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 da página chamar DeleteProduct
passando um para um ProductID
produto inexistente, a DELETE
instrução emitida para o banco de dados não terá nenhum efeito e, portanto, o DeleteProduct
método 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, devemos primeiro criar uma ProductsDataTable
instância e, em seguida, invocar seu NewProductRow()
método (o que fazemos em AddProduct
). Essa deficiência levanta a cabeça quando nos voltamos para inserir e atualizar produtos usando o ObjectDataSource. Em suma, o ObjectDataSource tentará criar uma instância dos parâmetros de entrada. Se o método BLL esperar 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 dos Fóruns ASP.NET a seguir: Atualizando ObjectDataSources com Strongly-Typed DataSets e Problema com ObjectDataSource e Strongly-Typed DataSet.
Em seguida, em 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 usam 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 no produto para 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 trabalham 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 DataObjectMethodAttribute
indica a finalidade do método. Como veremos em tutoriais futuros, o ObjectDataSource do ASP.NET 2.0 facilita o acesso declarativo aos 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 SuppliersBLL
método da UpdateSupplierAddress
classe. Esse método fornece uma interface para atualizar apenas as informações de endereço do fornecedor. Internamente, esse método lê no SupplierDataRow
objeto para o especificado supplierID
(usando GetSupplierBySupplierID
), define suas propriedades relacionadas ao endereço e, em seguida, chama para baixo no SupplierDataTable
método do .Update
O UpdateSupplierAddress
método segue:
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Update, true)]
public bool UpdateSupplierAddress
(int supplierID, string address, string city, string country)
{
Northwind.SuppliersDataTable suppliers =
Adapter.GetSupplierBySupplierID(supplierID);
if (suppliers.Count == 0)
// no matching record found, return false
return false;
else
{
Northwind.SuppliersRow supplier = suppliers[0];
if (address == null) supplier.SetAddressNull();
else supplier.Address = address;
if (city == null) supplier.SetCityNull();
else supplier.City = city;
if (country == null) supplier.SetCountryNull();
else supplier.Country = country;
// Update the supplier Address-related information
int rowsAffected = Adapter.Update(supplier);
// Return true if precisely one row was updated,
// otherwise false
return rowsAffected == 1;
}
}
Consulte o download deste artigo para obter minha implementação completa das classes BLL.
Etapa 2: acessar os conjuntos de dados tipado por meio das classes BLL
No primeiro tutorial, vimos exemplos de como trabalhar diretamente com o Conjunto de Dados Tipado programaticamente, mas com a adição de nossas classes BLL, a camada de apresentação deve funcionar na BLL. AllProducts.aspx
No exemplo do primeiro tutorial, o ProductsTableAdapter
foi usado para associar a lista de produtos a um GridView, conforme mostrado no código a seguir:
ProductsTableAdapter productsAdapter = new ProductsTableAdapter();
GridView1.DataSource = productsAdapter.GetProducts();
GridView1.DataBind();
Para usar as novas classes BLL, tudo o que precisa ser alterado é a primeira linha de código simplesmente substituir o ProductsTableAdapter
objeto por um ProductBLL
objeto :
ProductsBLL productLogic = new ProductsBLL();
GridView1.DataSource = productLogic.GetProducts();
GridView1.DataBind();
As classes BLL também podem ser acessadas declarativamente (assim como o Conjunto de Dados Tipado) usando o ObjectDataSource. Discutiremos o ObjectDataSource com mais detalhes nos tutoriais a seguir.
Figura 3: a lista de produtos é exibida em um GridView (clique para exibir a imagem em tamanho real)
Etapa 3: adicionando Field-Level validação às classes DataRow
A validação em nível de campo são verificações que pertencem 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
ProductID
campos ,ProductName
eDiscontinued
são necessários, mas todos os outros campos são opcionais - Os
UnitPrice
campos ,UnitsInStock
,UnitsOnOrder
eReorderLevel
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 ProductName
campos e QuantityPerUnit
são capturados pelos tipos de dados dessas colunas na Products
tabela (nvarchar(40)
e nvarchar(20)
, respectivamente). Se os campos são obrigatórios e opcionais são expressos por se a coluna da tabela de banco de NULL
dados permite s. Existem quatro marcar restrições que garantem que apenas valores maiores ou iguais a zero possam torná-lo nas UnitPrice
colunas , UnitsInStock
UnitsOnOrder
, ou 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 é obrigatório ou opcional já são capturados para o conjunto de DataColumns de cada DataTable. Para ver a validação de nível de campo existente fornecida automaticamente, vá para a Designer DataSet, selecione um campo de uma das DataTables e vá para o 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 ProductsDataRow
propriedade de como um valor de QuantityPerUnit
cadeia de caracteres maior que 20 caracteres, um ArgumentException
será gerado.
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 UnitPrice
valor deve ser maior ou igual a zero, por meio do janela Propriedades. Para fornecer esse tipo de validação em nível de campo, precisamos criar um manipulador de eventos para o evento ColumnChanging da DataTable. Conforme mencionado no tutorial anterior, os objetos DataSet, DataTables e DataRow criados pelo Typed DataSet 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.cs
.
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 ColumnChanging
evento que garante que os UnitPrice
valores de coluna , UnitsInStock
, UnitsOnOrder
e ReorderLevel
(se não NULL
) sejam maiores ou iguais a zero. Se qualquer coluna desse tipo estiver fora do intervalo, gere um ArgumentException
.
ProductsDataTable.ColumnChanging.cs
public partial class Northwind
{
public partial class ProductsDataTable
{
public override void BeginInit()
{
this.ColumnChanging += ValidateColumn;
}
void ValidateColumn(object sender,
DataColumnChangeEventArgs e)
{
if(e.Column.Equals(this.UnitPriceColumn))
{
if(!Convert.IsDBNull(e.ProposedValue) &&
(decimal)e.ProposedValue < 0)
{
throw new ArgumentException(
"UnitPrice cannot be less than zero", "UnitPrice");
}
}
else if (e.Column.Equals(this.UnitsInStockColumn) ||
e.Column.Equals(this.UnitsOnOrderColumn) ||
e.Column.Equals(this.ReorderLevelColumn))
{
if (!Convert.IsDBNull(e.ProposedValue) &&
(short)e.ProposedValue < 0)
{
throw new ArgumentException(string.Format(
"{0} cannot be less than zero", e.Column.ColumnName),
e.Column.ColumnName);
}
}
}
}
}
Etapa 4: Adicionar regras de negócios personalizadas às classes da BLL
Além da validação de 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 de negócios 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 fosse 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 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 alinhados!
Para impor essa regra de negócios no UpdateProducts
método , começaríamos verificando se Discontinued
foi definido true
como 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
.
public bool UpdateProduct(string productName, int? supplierID, int? categoryID,
string quantityPerUnit, decimal? unitPrice, short? unitsInStock,
short? unitsOnOrder, short? reorderLevel, bool discontinued, int productID)
{
Northwind.ProductsDataTable products = Adapter.GetProductByProductID(productID);
if (products.Count == 0)
// no matching record found, return false
return false;
Northwind.ProductsRow product = products[0];
// Business rule check - cannot discontinue
// a product that is supplied by only
// one supplier
if (discontinued)
{
// Get the products we buy from this supplier
Northwind.ProductsDataTable productsBySupplier =
Adapter.GetProductsBySupplierID(product.SupplierID);
if (productsBySupplier.Count == 1)
// this is the only product we buy from this supplier
throw new ApplicationException(
"You cannot mark a product as discontinued if it is the only
product purchased from a supplier");
}
product.ProductName = productName;
if (supplierID == null) product.SetSupplierIDNull();
else product.SupplierID = supplierID.Value;
if (categoryID == null) product.SetCategoryIDNull();
else product.CategoryID = categoryID.Value;
if (quantityPerUnit == null) product.SetQuantityPerUnitNull();
else product.QuantityPerUnit = quantityPerUnit;
if (unitPrice == null) product.SetUnitPriceNull();
else product.UnitPrice = unitPrice.Value;
if (unitsInStock == null) product.SetUnitsInStockNull();
else product.UnitsInStock = unitsInStock.Value;
if (unitsOnOrder == null) product.SetUnitsOnOrderNull();
else product.UnitsOnOrder = unitsOnOrder.Value;
if (reorderLevel == null) product.SetReorderLevelNull();
else product.ReorderLevel = reorderLevel.Value;
product.Discontinued = discontinued;
// Update the product record
int rowsAffected = Adapter.Update(product);
// Return true if precisely one row was updated,
// otherwise false
return rowsAffected == 1;
}
Respondendo a erros de validação na camada de apresentação
Ao chamar a BLL da camada de apresentação, podemos decidir se deseja lidar com exceções que possam ser geradas ou deixá-las surgir até ASP.NET (o que gerará o HttpApplication
evento do Error
). Para lidar com uma exceção ao trabalhar com a BLL programaticamente, podemos usar uma tentativa... catch block, como mostra o exemplo a seguir:
ProductsBLL productLogic = new ProductsBLL();
// Update information for ProductID 1
try
{
// This will fail since we are attempting to use a
// UnitPrice value less than 0.
productLogic.UpdateProduct(
"Scott s Tea", 1, 1, null, -14m, 10, null, null, false, 1);
}
catch (ArgumentException ae)
{
Response.Write("There was a problem: " + ae.Message);
}
Como veremos em tutoriais futuros, o tratamento de exceções que surgirem 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 projetado é criado em camadas distintas, cada uma encapsulando uma função específica. No primeiro tutorial desta série de artigos, criamos uma Camada de Acesso a Dados usando Conjuntos de Dados Tipado; neste tutorial, criamos uma Camada de Lógica de Negócios como uma série de classes na pasta do App_Code
nosso aplicativo que fazem chamadas para nosso DAL. A BLL implementa a lógica de nível de campo e de nível de negócios para nosso aplicativo. Além de criar uma BLL separada, como fizemos neste tutorial, outra opção é estender os métodos de 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 de forma tão limpa quanto a abordagem que adotamos neste artigo.
Com a DAL e a BLL concluídas, estamos prontos para começar em 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.
Programação feliz!
Sobre o autor
Scott Mitchell, autor de sete livros do 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 contatado em mitchell@4GuysFromRolla.com. ou através de seu blog, que pode ser encontrado em http://ScottOnWriting.NET.
Agradecimentos Especiais
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? Nesse caso, deixe-me uma linha em mitchell@4GuysFromRolla.com.