Vamos agora desenvolver nosso projeto de teste que será um Console Application.

Como foi dito no início do artigo, iremos utilizar o conceito de três camadas (Model, Business e Data Access). Dessas três camadas, iremos codificar apenas a Model e a Business, pois utilizaremos nossa classe DAL Genérica.

No seu Visual Studio 2008, dentro da nossa solução, crie mais um projeto de nome “TestProject” do tipo Console Application.

Vamos primeiramente montar a estrutura de pastas deste projeto:


Ilustração 12 – Estrutura de pastas do projeto de teste

Dentro do projeto temos uma pasta BO (anacrônico para Business Objects), onde ficarão as classes da camada Business, e temos uma pasta onde ficarão as classes da camada Model (ou Entities). Temos também um App.Config, que conterá as strings de conexão e algumas chaves na seção appconfig. A classe Program.cs será o Console Application propriamente dito.

Vamos iniciar a codificação pelo App.Config:


xml version="1.0" encoding="utf-8" ?>



  

    

  appSettings>

  

    

    

  connectionStrings>

configuration>

Este é o nosso arquivo de configuração. Ele é bem simples. Possui apenas a chave “current.connectionna seção <appSettings>, necessária para o funcionamento da nossa classe DAL genérica. Essa chave aponta para a string de conexão que será a mais utilizada pela nossa aplicação.

Na seção <connectionStrings> temos duas conection strings, uma que será a principal “sqlServerConn”, que é a que utilizaremos no nosso primeiro exemplo, e outra de nome “sqlServerConn2” que utilizaremos mais tarde.

Agora precisamos adicionar as referências dos outros projetos da solução ao projeto de teste. Clique com o botão direto do mouse no projeto de teste, depois clique em “add reference” na aba “Projects” selecione todos os projetos e clique em “OK”.


Ilustração 13 – Referências a serem adicionadas ao projeto de teste

Agora que temos todas as referências que precisamos, vamos codificar nossa classe model. No nosso BD temos apenas uma tabela e cinco procedures. Vamos então criar uma classe para representar essa tabela e utilizar os Atributos customizados que construimos, para mapear as propriedades dessa classe com os campos da tabela e com os parâmetros das Stored Procedures.

Clique com o botão direito do mouse na pasta “Model” e adicione uma nova classe de nome “Produto.cs”.

Vale lembrar que, por ser uma classe da camada Model, esta classe não conterá nenhum método, somente propriedades. Primeiramente adicione os seguintes “usings”:


using System;

using System.Collections.Generic;

using System.Text;

using System.Data;

using GenDAL.Library.Attributes;

using GenDAL.Library.BaseClasses;

Logo após declaramos a namespace da classe model. Se não estiver assim, corrija:



namespace TestProject.Model

{

 

}

Vamos agora declarar a classe:


[AttProcNameSelectNoFilter("spSELProd_S")]

[AttDataAccessType(DataAccesType.Both)]

public class Produto : BaseModelClass

{

 

}

Utilizamos dois de nossos atributos customizados - AttProcNameSelectNoFilter que serve para indicar qual é a procedure que realiza um Select sem filtro e AttDataAccessType que serve para indicar qual será o tipo de acesso a dados que a classe DAL realizará. No caso deixamos setado para Both, que quer dizer que a classe DAL poderá acessar o BD via SP ou via execução de SQL. Repare que a classe herda de BaseModelClass. Essa herança é necessária para conseguirmos utilizar a classe DAL genérica e esta utilize o método de validação de uso dos atributos que criamos.

Vamos agora criar nossos campos privados que serão o reflexo dos campos da tabela e dos parâmetros esperados pelas SPs que criamos:



#region Fields

 

private int _id;

private string _nome;

private string _url;

private string _desc;

private decimal _preco;

private string _imagem;

private byte[] _imagemByte;

 

#endregion

Vamos agora criar as propriedades de acesso aos campos privados da nossa classe. Aqui utilizaremos os nossos atributos customizados para mapear essas propriedades com os campos da tabela e com os parâmetros das SPs.


#region Properties

        [AttParamProcDB(ProcedureTypes.Delete, "spDELProd_D", "@pProdId", DbType.Int32, ParameterDirection.Input)]

        [AttParamProcDB(ProcedureTypes.Update, "spUPDProd_U", "@pProdId", DbType.Int32, ParameterDirection.Input)]

        [AttParamProcDB(ProcedureTypes.SelectFilter, "spSELProd_SF", "@pProdId", DbType.Int32, ParameterDirection.Input)]

        [AttParamProcDB(ProcedureTypes.Insert, "spINSProd_I", "@outProdId", DbType.Int32, ParameterDirection.Output, 50)]

        [AttFieldDB("prod_id")]

        public int Id

        {

            get { return _id; }

            set { _id = value; }

        }

 

        [AttParamProcDB(ProcedureTypes.Update, "spUPDProd_U", "@pProdNome", DbType.String, ParameterDirection.Input)]

        [AttParamProcDB(ProcedureTypes.Insert, "spINSProd_I", "@pProdNome", DbType.String, ParameterDirection.Input)]

        [AttFieldDB("prod_nome")]

        public string Nome

        {

            get { return _nome; }

            set { _nome = value; }

        }

 

        [AttParamProcDB(ProcedureTypes.Update, "spUPDProd_U", "@pProdUrl", DbType.String, ParameterDirection.Input)]

        [AttParamProcDB(ProcedureTypes.Insert, "spINSProd_I", "@pProdUrl", DbType.String, ParameterDirection.Input)]

        [AttFieldDB("prod_url")]

        public string Url

        {

            get { return _url; }

            set { _url = value; }

        }

 

        [AttParamProcDB(ProcedureTypes.Update, "spUPDProd_U", "@pProdDesc", DbType.String, ParameterDirection.Input)]

        [AttParamProcDB(ProcedureTypes.Insert, "spINSProd_I", "@pProdDesc", DbType.String, ParameterDirection.Input)]

        [AttFieldDB("prod_desc")]

        public string Desc

        {

            get { return _desc; }

            set { _desc = value; }

        }

 

        [AttParamProcDB(ProcedureTypes.Update, "spUPDProd_U", "@pProdPreco", DbType.Decimal, ParameterDirection.Input)]

        [AttParamProcDB(ProcedureTypes.Insert, "spINSProd_I", "@pProdPreco", DbType.Decimal, ParameterDirection.Input)]

        [AttFieldDB("prod_preco")]

        public decimal Preco

        {

            get { return _preco; }

            set { _preco = value; }

        }

 

        [AttParamProcDB(ProcedureTypes.Update, "spUPDProd_U", "@pProdImg", DbType.String, ParameterDirection.Input)]

        [AttParamProcDB(ProcedureTypes.Insert, "spINSProd_I", "@pProdImg", DbType.String, ParameterDirection.Input)]

        [AttFieldDB("prod_img")]

        public string Imagem

        {

            get { return _imagem; }

            set { _imagem = value; }

        }

 

        [AttParamProcDB(ProcedureTypes.Update, "spUPDProd_U", "@pProdImgByte", DbType.Binary, ParameterDirection.Input)]

        [AttParamProcDB(ProcedureTypes.Insert, "spINSProd_I", "@pProdImgByte", DbType.Binary, ParameterDirection.Input)]

        [AttFieldDB("prod_img_byte")]

        public byte[] ImagemByte

        {

            get

            {

                if (_imagemByte == null)

                {

                    _imagemByte = new byte[0];

                }

                return _imagemByte;

            }

            set { _imagemByte = value; }

        }

        #endregion

Repare que para usarmos o atributo AttParamProcDB precisamos informar o tipo de procedure, o nome da procedure, o nome do parâmetro que será mapeado com a propriedade, o tipo de dado do parâmetro e a direção do parâmetro. Para utilizarmos o atributo AttFieldDB precisamos declarar apenas o nome do campo da tabela. Com isso mapeamos nossa classe com a tabela Produto do BD assim como com as procedures que farão manipulações nessa tabela. Em outras palavras fizemos um mapeamento Objeto/Relacional

Por último vamos construir um construtor vazio:


#region Constructor

 

public Produto()

{

 

}

 

#endregion

Vamos agora construir nossa classe da camada Business. Vale lembrar que a classe Business, não deve possuir nenhum código de acesso a dados, deve ter apenas a inteligência de negócio da aplicação. Todo código de acesso a dados estará encapsulado na nossa classe DAL genérica.

Clique com o botão direito do mouse na pasta BO e adicione uma nova classe de nome “ProdutoBO”.

Vamos começar com as referências que precisamos:



using System;

using System.Collections.Generic;

using GenDAL.Library.DataBaseLib.GenericDAL;

using TestProject.Model;

Precisamos de apenas 4 referências. As 2 primeiras são do próprio .Net. As duas últimas são dos nossos projetos.

Vamos declarar a namespace e a classe:


namespace TestProject.BO

{

     public class ProdutoBO : GenericDAL

     {

 

     }

 

}

Na declaração da classe dizemos que ProdutoBO herdará da nossa classe DAL genérica. Lembrando que ao declarar essa herança, precisamos passar um Type Parameter. Esse type parameter precisa ser uma implementação da nossa interface “IBaseModel” e precisa ter um construtor vazio. Então temos que passar a classe model “Produto” que herda de BaseModelClass, que é uma implementação de IBaseModel e possui um construtor vazio.

Pronto com isso teremos acesso aos métodos de acesso a dados da classe DAL, não precisando codificar nenhuma classe DAL. Na reaIidade, o uso desta técnica deixa a classe DAL genérica preparada para atender às exigências da classe business que estamos construindo. A classe DAL, por sua vez, possui a funcionalidade de ler algumas informações contidas em nossos atributos a respeito da classe model corrente. Com essas informações a classe DAL consegue “tomar a decisão” de quais métodos executar para atender às exegências da classe Business que a esta consumindo. Isso nos deixa livre para focarmos nas regras de negócio do nosso projeto.

Vamos ao primeiro método da nossa classe Business. Esse método acessará a procedure que realiza um select sem filtros e retornara um List do tipo Produto, relativo ao resultado da busca dessa procedure:


public List GetListaSemFiltro()

{

  List retorno = null;

  using (base.TransactionScope())

  {

      retorno = base.ExecuteProcSelectNoFilter();

  }

  return retorno;

 

}

Um detalhe interessante é o conceito de “TransactionScope”. Definimos anteriormente que todo acesso a dados deverá estar dentro de um contexto de transação, como evidenciado no bloco de comando:


using (base.TransactionScope())

      {

          retorno = base.ExecuteProcSelectNoFilter();

      }

Outro detalhe interessante é que a utilização de type parameters, faz o compilador em tempo de design entender quais são os tipos de retorno e os tipos de parâmetros utilizados nos métodos da classe DAL genérica. Para verificar, deixe o cursor do mouse sobre a declaração do método:


Ilustração 14 – Exemplo de como o compilador interpreta o uso de Type Parameters

O Visual Studio mostra o retorno do método como sendo um List.

Vamos criar um novo método que fará duas operações dentro de um TransactionScope:


public void TestaInsertUpdateTrans(Produto pObj)

{

  using (base.TransactionScope())

  {

    this.ExecuteProcInsert(pObj);

    pObj.Nome = "teste Transaction";

    this.ExecuteProcUpdate(pObj);

             

   }

            

 }

Este método chama as procedures de insert e um update dentro de uma mesma transação. Claro que isto é apenas um exemplo, pois numa situação real, seria muito improvável fazer um update logo após um insert. O intuito aqui é demostrar que com o conceito de TransactionScope, temos condições de realizar todos as nossas operações dentro de transações, sem nos preocuparmos em criar os objetos do ado.net de transação e conexão.


No próximo artigo, codificaremos a classe program.cs para iniciarmos os testes.