5 de 7 pessoas classificaram isso como útil - Avalie este tópico

Criando uma camada de dados com o .NET 2.0

Publicado em: 14 de dezembro de 2006
Por Dennes Torres

Nesta página

Introdução
Primeira etapa : Criando uma camada de acesso a dados simples
Segunda Etapa : Criando um método personalizado
Terceira Etapa : Implementando uma regra de negócio personalizada
Quarta Etapa : Implementando regras nos métodos padrões
Quinta Etapa : Preparando o componente para uso em aplicações windows
Sexta Etapa : Criando a aplicação Windows
Sétima Etapa : Implementando regras de negócio para o ambiente Windows e Web
Conclusões finais

Introdução

Anteriormente falamos sobre a criação de uma camada de dados no artigo em http://www.bufaloinfo.com.br/artigos/artigo281104.asp . O que explicamos neste artigo continua válido para o .NET 2.0, com mudanças mínimas. Assim sendo esta forma que apresentamos anteriormente ainda é válida, mas as mudanças existentes no .NET 2.0 fizeram com que esta não seja sempre a melhor opção.

No .NET 2.0 temos um volume maior de recursos para aumentar a produtividade na interface. Entre esses recursos temos o ObjectDataSource, que permite fazermos um vinculo de dados entre a interface e um objeto, tudo feito de forma visual.

Neste ponto começam nossos problemas. Os componentes de negócio produzidos pela metodologia anterior não se encaixam com o ObjectDataSource, o que faz com que tenhamos que dispensar alguns dos novos recursos que geram alta produtividade no .NET 2.0.

Mas temos alternativas, é disso que vamos tratar neste artigo. Vamos criar uma camada de negócios e dados utilizando o .NET 2.0 e vamos compara-la com o que criamos anteriormente no .NET 1.1

Primeira etapa : Criando uma camada de acesso a dados simples

Crie um novo webSite

Adicione um novo projeto do tipo ClassLibrary, vamos chama-lo de libDados

Na libDados, utilize o add new item para adicionar um novo dataset chamado dsProdutos

Pela ToolBox, adicione um novo TableAdapter

Utilize a seguinte query para montar o TableAdapter :

SELECT ProductID, ProductName, UnitPrice, UnitsInStock
	FROM Products
			

Nas advanced options, desmarque a opção optimistic concurrency.

Aa581776.artigo26062006-01(pt-br,MSDN.10).gif

Na tela para escolha dos métodos a serem gerados, deixe as opções default marcadas.

Aa581776.artigo26062006-02(pt-br,MSDN.10).gif

O TableAdapter é um novo objeto que acompanha as dataTables e é criado de forma personalizada para cada dataTable, conforme desejarmos.

O TableAdapter gera métodos para realizar a leitura e gravação de dados na base de dados.

Clique com o botão direito sobre o Fill do TableAdapter e selecione a opção Add Query

Aa581776.artigo26062006-03(pt-br,MSDN.10).gif

Selecione o tipo de query como "Update"

Aa581776.artigo26062006-04(pt-br,MSDN.10).gif

Altere a query de Update, mudando o parâmetro de @Original_productId para @ProductID

Aa581776.artigo26062006-09(pt-br,MSDN.10).gif

O método de update inicialmente gerado junto do Fill é feito para receber dois parâmetros chave, no nosso exemplo, ProductID e @ProductID. Isso não é adequado e dificulta o vinculo deste método com objetos visuais. Criando um método a parte como este que acabamos de criar, resolvemos o problema.

Aa581776.artigo26062006-10(pt-br,MSDN.10).gif

No site web, crie uma referência para o projeto libDados

Aa581776.artigo26062006-05(pt-br,MSDN.10).gif

Adicione um ObjectDataSource na página default.aspx

Configure o objectDataSource, apontando para o TableAdapter

Aa581776.artigo26062006-06(pt-br,MSDN.10).gif

Observe que o TableAdapter não só é visível como possui os métodos adequados para se interligar automaticamente com o ObjectDataSource - exceto no caso do Update

Altere o método de Update para o nosso método Atualizar

Aa581776.Artigo26062006-08(pt-br,MSDN.10).gif

Nas propriedades do objectDataSource, altere a propriedade OldValuesParametersFormatString deixando apenas como "{0}"

Adicione uma dataGrid, vincule-a ao ObjectDataSource e teste, incluindo a edição na grid.

Aa581776.artigo26062006-07(pt-br,MSDN.10).gif

Resultado: Neste ponto temos a interface da aplicação fazendo todo o acesso a dados através de um componente, o tableAdapter, sem acesso direto ao banco.

Uma das muitas questões que ficam pendentes é se essa estrutura nos trará os benefícios de manutenção que temos como objetivo ao utilizar uma estrutura em camadas. Na sequencia de exemplos a seguir vamos responder a esta pergunta.

Segunda Etapa : Criando um método personalizado

Vamos criar em nossa aplicação o recurso de atualizar o preço dos produtos com base em um percentual informado e vamos fazer isso através de nosso objeto de negócios.

Crie uma nova query no tableAdapter

Defina o tipo da query como de Update

Utilize a seguinte query de Update :

Update Products set UnitPrice = Unitprice + (unitprice *@percentual/100)
			

Defina o nome do método como AtualizarPreco

Aa581776.artigo26062006-11(pt-br,MSDN.10).gif

Na página, adicione um novo ObjectDataSource

Configure o ObjectDataSource apontando para nosso TableAdapter

Selecione como método de Update o nosso AtualizarPreco

Insira uma textbox e um botão na página

Aa581776.artigo26062006-12(pt-br,MSDN.10).gif

Chamaremos a textbox de txtPercentual e o botão de cmdAtualizarPreco. Você pode também inserir um requiredFieldValidator.

Configure a propriedade UpdateParameters do ObjectDataSource, aponte para a textbox.

Aa581776.artigo26062006-13(pt-br,MSDN.10).gif

Programe o click do botão da seguinte forma :

   39     Protected Sub cmdAtualizarPreco_Click(ByVal sender As Object, 
			  ByVal e As System.EventArgs) Handles cmdAtualizarPreco.Click
   40 
   41         ObjectDataSource2.Update()
   42         GridView1.DataBind()
   43 
   44     End Sub
			

A instruçào Update provoca a atualização na base de dados, sendo que o objectDataSource já tem o parâmetro configurado - a textbox - enquanto que o dataBind da gridView faz um refresh nos dados.

Com isso vemos que podemos criar métodos personalizados para realizar tarefas personalizadas no banco. Mas até o momento a realização de tarefas do componente é feita diretamente com o banco, exatamente como um componente de acesso a dados. Como fazer então para implementar regras de negócio que exijam mais código ?

Terceira Etapa : Implementando uma regra de negócio personalizada

Vamos implementar uma regra de validação personalizada para o percentual de aumento. Será uma regra simples, mas demonstrará como regras diversas podem ser implementadas com o TableAdapter

Utilize o Add New Item e adicione uma nova classe

Troque o nome da classe para ProductsTableAdapter

Crie o nameSpace dsProdutosTableAdapters e deixe a classe dentro deste nameSpace

Os tableAdapters são sempre criados como Partial Class, isso nos permite extender as funcionalidades do TableAdapter criando uma outra metade para ele, como estamos fazendo.

No TableAdapter, altere a propriedade Modifiers de nosso método AtualizarPreco para Private

Aa581776.artigo26062006-14(pt-br,MSDN.10).gif

Não existe Partial Method, não podemos alterar o funcionamento do método que já criamos, mas podemos oculta-lo tornando-o private e criar um novo método.

Observe que mesmo definindo o método como private nós podemos chama-lo, pois estamos criando uma partial Class, ou seja, estamos trabalhando dentro da própria classe productsTableAdapter, então o escopo private ainda é acessível.

Altere o nome do método de AtualizarPreco para privAtualizarPreco

O nome do método público precisa ser mantido igual para que a interface não tenha que mudar, então alteramos o nome do método privado.

Crie um novo método AtualizarPreco com a mesma assinatura e com a seguinte implementação :

   46     Public Sub AtualizarPreco(ByVal Percentual As Decimal)
   47 
   48         If Percentual > 50 Then
   49             Throw New ApplicationException("Percentual muito alto")
   50         End If
   51 
   52         Me.privAtualizarPreco(Percentual)
   53 
   54     End Sub
			

Não existe nenhuma restrição técnica que determine que a assinatura precisa ser igual. De fato em alguns casos você desejará fazer uma assinatura diferente, quando o próprio método de negócio será responsável por gerar algumas das informações que serão gravadas.

Mas no caso de manutenção, quando a interface gráfica já existe e está vinculada ao método existente, se a assinatura for alterada a interface para de funcionar.

No client, apenas para deixar a interface agradável, vamos adicionar um tratamento de erro na atualização :

   56     Protected Sub cmdAtualizarPreco_Click(ByVal sender As Object, 
				ByVal e As System.EventArgs) Handles cmdAtualizarPreco.Click
   57 
   58         Try
   59             ObjectDataSource2.Update()
   60         Catch ex As Exception
   61             Me.ClientScript.RegisterClientScriptBlock(Me.GetType(), 
					"xxx", "alert('" & ex.Message & "')", True)
   62 
   63         End Try
   64         GridView1.DataBind()
   65 
   66     End Sub
			

Com isso adicionamos uma nova regra de negócio e obtivemos exatamente o que se espera de um desenvolvimento em camadas : Facilidade de manutenção. Foi possível adicionar esta regra sem que a interface client tivesse que ser alterada.

Quarta Etapa : Implementando regras nos métodos padrões

Quando criamos o TableAdapter foram criados os métodos Fill e GetData. Junto desses métodos, foram criados métodos de atualização (insert/update/delete), sendo que nos exemplos anteriores chegamos a substituir o método de update por um método nosso, personalizado.

A questão é como fazer para implementar regras de negócio nestes métodos.

Assim como alteramos o modifers de nosso método personalizado, no exemplo anterior, poderiamos fazer o mesmo com estes métodos. Mas existe um problema : Junto ao Fill e GetData, só podemos alterar o modifier dos dois, Fill e GetData. Não podemos alterar o Modifier dos métodos de atualização que foram gerados junto do Fill e GetData.

Nesse caso o melhor é adotarmos um padrão : Não utilizar os métodos de atualização criados junto com o Fill/GetData e sim criar nossas próprias querys de atualização a parte. Então, durante o wizard para criação do Fill, desmarcamos a opção GenerateDbDirectMethods.

Aa581776.artigo26062006-15(pt-br,MSDN.10).gif

Com isso podemos criar nossas próprias querys de atualização (utilizando o Add Query) e implementar regras de negócio da mesma forma que fizemos nos exemplos anteriores.

Quinta Etapa : Preparando o componente para uso em aplicações windows

As aplicações windows possuem uma importante diferença em relação a aplicações web que precisa ser considerada para a montagem deste componente : Enquanto as aplicações web atualizam registros individualmente, as aplicações windows atualizam (e aqui falo de insert/update/delete) blocos de registros utilizando o dataSet.

O TableAdapter possui diferentes métodos de atualização exatamente prevendo as diferentes formas de atualização entre aplicações web e windows. Para aplicações web o TableAdapter possui os DbDirectMethods, enquanto que para aplicações Windows o TableAdapter gera métodos de update que recebem como parâmetro a DataTable ou DataSet e fazem a atualização de todos os dados no banco. São 2 métodos de Update em overloads.

Esses métodos, porém, nos causarão problema : Não temos o controle do modifers destes métodos, pois são gerados juntamente com o Fill/GetData. Também não temos uma forma direta de "desligar" a geração destes métodos, como fizemos com os DbDirectMethods.

É importante lembrar neste ponto que o TableAdapter mantém um DataAdapter - o mesmo dataAdapter que utilizávamos na versão 1.1 - configurado com as querys que serão executadas no banco.

A solução para "desligar" a geração dos métodos de atualização para o ambiente windows é desligando a geração do insert/update/delete nas advanded options da geração do método Fill. O problema é que isso desliga também a configuração do DataAdapter interno para a realização de atualizações na base.

Aa581776.artigo26062006-16(pt-br,MSDN.10).gif

Nesse caso somos obrigados a criar não só o método de atualização, mas um método que faça a configuração do DataAdapter com as querys de atualização. Para isso podemos utilizar nosso velho amigo CommandBuilder, que na versão 2.0 aprendeu alguns truques novos.

Veja como fica o código em nosso productsTableAdapter :

   68     Public Sub AtualizarProdutos(ByVal dados As dsProdutos.ProductsDataTable)
   69 
   70         InicializarAdapter()
   71         Me._adapter.Update(dados)
   72     End Sub
   73 
   74 
   75     Private Sub InicializarAdapter()
   76         Dim cb As SqlClient.SqlCommandBuilder
   77 
   78         Me._adapter.SelectCommand = Me.CommandCollection(0)
   79 
   80         cb = New SqlClient.SqlCommandBuilder(Me._adapter)
   81         cb.ConflictOption = ConflictOption.CompareAllSearchableValues
   82 
   83         Me._adapter.DeleteCommand = cb.GetDeleteCommand
   84         Me._adapter.UpdateCommand = cb.GetUpdateCommand
   85         Me._adapter.InsertCommand = cb.GetInsertCommand
   86     End Sub
			

No inicializarAdapter utilizamos o commandBuilder para fazer a configuração do dataAdapter, enquanto que no AtualizarProdutos tomamos o cuidado de chamar o inicializarAdapter antes de fazer a atualização. A variável "_adapter" é private, mas nós estamos fazendo uma partial class, portanto ela ainda encontra-se dentro do escopo.

Observe a propriedade ConflictOption, no CommandBuilder. Na versão anterior a forma de tratamento de conflitos não era configurável, isso mudou na versão 2.0, com essa propriedade.

Neste exemplo está sendo determinado que a clausula where das instruções update e delete contenham todos os campos, de forma que se houverem atualizações simultâneas os registros não sejam sobrescritos, não havendo perda de dados.

No método AtualizarProdutos podemos ainda implementar outros recursos com relação a gravação, tal como gravação transacional, controle de campos auto-numeração, etc. Veja em http://www.bufaloinfo.com.br/dicas.asp?cod=611 , http://www.bufaloinfo.com.br/dicas.asp?cod=630 e http://www.bufaloinfo.com.br/dicas.asp?cod=657

Sexta Etapa : Criando a aplicação Windows

Adicione na solução um novo projeto Windows

Defina este projeto como startUp Project

Aa581776.artigo26062006-17(pt-br,MSDN.10).gif

Adicione uma referência para a libDados

Abra a janela de Data Sources em Data->Show Data Sources

Aa581776.Artigo26062006-18(pt-br,MSDN.10).gif

Adicione um novo data source

No tipo, escolha "Object"

Aa581776.artigo26062006-19(pt-br,MSDN.10).gif

Na seleção de objetos, aponte para o dsProducts na libDados

Aa581776.artigo26062006-20(pt-br,MSDN.10).gif

Bem diferente da aplicação web, aqui apontamos para o DataSet e não para o objeto que possui os métodos de negócio.

Arraste a tabela Products que apareceu na janela Data Sources para dentro do formulário

Verifique se ela está com a opção "Details" selecionada como formato de geração.

Aa581776.artigo26062006-21(pt-br,MSDN.10).gif

Clique com o botão direito no ProductsBindNavigator e selecione "Edit Items"

Aa581776.artigo26062006-22(pt-br,MSDN.10).gif

Habilite o botão SaveItem

Aa581776.artigo26062006-23(pt-br,MSDN.10).gif

Codifique o form_load para carregar os dados para o dataSet

   88     Dim obj As New libDados.dsProdutosTableAdapters.productsTableAdapter
   89 
   90     Private Sub Form1_Load(ByVal sender As System.Object, 
			ByVal e As System.EventArgs) Handles MyBase.Load
   91 
   92         obj.Fill(DsProdutos.Products)
   93     End Sub
			

Com um duplo clique no botão salvar, que encontra-se no topo do formulário, codifique a gravação dos dados :

   96     Private Sub ProductsBindingNavigatorSaveItem_Click(ByVal sender As System.Object, 
  ByVal e As System.EventArgs) Handles ProductsBindingNavigatorSaveItem.Click
   97 
   98         ProductsBindingSource.EndEdit()
   99         obj.AtualizarProdutos(DsProdutos.Products)
  100         MsgBox("Todos os produtos foram atualizados")
  101     End Sub
			

Observe o uso do EndEdit para garantir a gravação do registro atual, caso ele esteja em edição.

Sétima Etapa : Implementando regras de negócio para o ambiente Windows e Web

O grande desafio da implementação de regras é evitar que o código dessas regras seja duplicado.

No ambiente web, utilizamos os métodos de atualização direta, recebendo cada campo como um parâmetro. No ambiente windows, recebemos uma dataTable que pode conter diversos registros. Formatos diferentes de dados. Portanto nosso desafio é validar ambos sem duplicar a lógica de validação.

Precisaremos então ter um método privado de validação de dados. Este método privado de validação será chamado tanto da atualização direta como da atualização com dataTables.

Porém na implementação temos que escolher uma entre duas opções :

  • O método que recebe a dataTable pode chamar o método de validação uma vez para cada registro atualizado, passando os valores dos campos ou

  • O método de atualização direta cria um dataSet e dataTable e gera uma nova linha, passando esta dataTable para o método de validação

Assim sendo, em um dos casos precisa haver uma conversão do formato dos dados.

Veja um exemplo a seguir. Neste caso convertemos os dados do dataSet, chamando a validação linha a linha :

  104     Private Sub ValidarDados(ByVal productName As String, ByVal unitPrice As Decimal, 
  ByVal UnitsInStock As Integer, ByVal ProductId As Integer)
  105 
  106         If unitPrice > 1000 Then
  107             Throw New ApplicationException("O preço é muito alto")
  108         End If
  109     End Sub
  110 
  111     Public Sub AtualizarProdutos(ByVal dados As dsProdutos.ProductsDataTable)
  112         InicializarAdapter()
  113 
  114         Dim dt As DataTable
  115         dt = dados.GetChanges()
  116 
  117         For Each dr As dsProdutos.ProductsRow In dt.Rows
  118             ValidarDados(dr.ProductName, dr.ProductID, dr.UnitsInStock, dr.ProductID)
  119 
  120         Next
  121 
  122         Me._adapter.Update(dados)
  123     End Sub
  124 
  125     Public Sub InserirDados(ByVal productid As Integer, 
  ByVal productName As String, ByVal unitPrice As Decimal, ByVal unitsInStock As Integer)
  126 
  127         Me.ValidarDados(productName, unitPrice, unitsInStock, productid)
  128 
  129         Me.privInserirDados(productid, productName, unitPrice, unitsInStock)
  130 
  131     End Sub
			

Conclusões finais

Modelo em Camadas versão 1.1

Modelo em Camadas versão 2.0

Permite a troca de servidor de banco sem necessidade de manutenção (excetuando-se querys SQL) e mantendo a melhor performance possível

Fica vinculado a um único servidor de banco

Independe da ferramenta de desenvolvimento

Vinculo direto com a ferramenta de desenvolvimento

Trabalha de forma não tipada, permitindo a inclusão/exclusão de campos dinamicamente apenas com manutenção no banco e interface

Por trabalhar de forma tipada, exige a manutenção e recompilação da camada de negócios em caso de mudança nos campos

Não se integra com a interface gráfica, exigindo considerável codificação na interface, além da própria codificação na camada de negócios

Totalmente integrado com a interface, não só evita o código no desenvolvimento do client como também na camada de negócios

Como você pode observar neste quadro comparativo, o modelo que criamos para a versão 1.1 tem várias vantagens em relação ao novo modelo que acabei de apresentar.

Mas a única desvantagem pode ser decisiva : o modelo anterior não se integra com a nova interface gráfica, enquanto que o novo evita até mesmo boa parte da codificação nas camadas de componentes.

Boa parte das aplicações no .NET 2.0 podem ser produzidas neste novo modelo com vantagens para a produtividade e manutenção. Certamente apenas algumas, as maiores e com necessidades específicas, precisarão utilizar os recursos mais versáteis do modelo anterior.

Dennes Torres MCAD,MCSD,MCSE,MCDBA

Isso foi útil para você?
(1500 caracteres restantes)
A Microsoft está realizando uma pesquisa online para saber sua opinião sobre o site do MSDN. Se você optar por participar, a pesquisa online lhe será apresentada quando você sair do site do MSDN.

Deseja participar?