ABC App – 03 Fazendo um Refactoring na DAL
No post anterior, iniciamos a contrução da ABC App, construindo uma camada de Domínio e uma camada de DAL. Antes de continuarmos a desenvolver, vamos aplicar um Refactoring?
Eu não gosto de termos em inglês para coisas que podemos falar em português, mas no caso do Refactoring eu abro uma exceção, normalmente o que se fala é Refatoração, mas essa palavra para mim é um termo matemático. O correto, IMHO, seria dizer Re-fabricar, mas fico com o termo em inglês.
E o que podemos fazer?
Vamos seguir usando Baby Steps, daqui por diante pequenos passos, ou seja , faço Refactoring e testo, Refactoring e testo, então não corro o risco de fazer uma grande alteração e me perder no código alterado e o sistema não funcionar. A primeira coisa para alterar é a criação da conexão com o BD, como será recorrente em toda classe DAL, podemos criar um classe base que todas dessa camada herdem e, assim, centralizarmos o código. Então, primeira coisa, crie uma classe BaseDAL, como abaixo:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.Common;
using System.Resources;
using EntLib = Microsoft.Practices.EnterpriseLibrary.Data;
namespace ABCApp.DAL
{
public abstract class BaseDAL
{
public EntLib.Database db { get; set; }
public BaseDAL()
{
db = EntLib.DatabaseFactory.CreateDatabase("Connection String");
}
}
}
Listagem 01
A classe BaseDAL não poderá ser instanciada, só poderá ser herdada. Com isso, ganhamos um único ponto de conexão com o Banco de Dados, que será muito útil quando quisermos controlar um transação, outro ponto é no reuso de código. Para usarmos essa classe vamos herdar ela na classe Customer da DAL. Não se esqueça de apagar a linha indicada, pois agora usaremos o objeto Database da classe BaseDAL. Lembram-se do código abaixo?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Domain = ABCApp.Domain;
using System.Data;
using System.Data.Common;
using System.Resources;
using Microsoft.Practices.EnterpriseLibrary.Data;
namespace ABCApp.DAL
{
public class Customer : BaseDAL
{
public Domain.Customer GetCustomerById(int customerId)
{
//Apague a linha abaixo
//Database db = DatabaseFactory.CreateDatabase("Connection String");
string sql = "select customerid, firstname, middlename, lastname, companyname, emailaddress, phone, modifieddate from saleslt.customer where customerid = @customerid";
DbCommand cmd = db.GetSqlStringCommand(sql);
db.AddInParameter(cmd, "customerid", DbType.Int32, customerId);
IDataReader dr = db.ExecuteReader(cmd);
Domain.Customer c = new Domain.Customer();
while (dr.Read())
{
c.CompanyName = dr["companyname"].ToString();
c.CustomerId = Convert.ToInt32(dr["customerid"]);
c.EmailAddress = dr["emailaddress"].ToString();
c.FirstName = dr["firstname"].ToString();
c.LastName = dr["lastname"].ToString();
c.MiddleName = dr["middlename"].ToString();
c.ModifiedDate = Convert.ToDateTime(dr["modifieddate"]);
c.Phone = dr["phone"].ToString();
}
if (!dr.IsClosed)
dr.Close();
return c;
}
}
}
Listagem 02
Se já tivéssemos escrito mais classes DAL, o impacto dessa alteração seria muito maior. Então , seguindo nossso mantra de pequenos passos, vamos executar o código e saber se ele continua funcionando como o esperado.
Sendo o retorno igual ao que você tinha antes de modificar o código, então passamos neste Refactoring!
Vamos continuar implementando outros métodos na nossa classe DAL, o próximo interessante é retornar uma coleção de clientes. Podemos retornar todos os clientes e basicamente o método seria o mesmo do anterior mas sem o parâmetro de customerId e sem a linha de parâmetro no Command, vou criar então o método GetCustomers e que irá me retornar uma lista tipada de objetos, segue:
public IList<Domain.Customer> GetCustomers()
{
List<Domain.Customer> lstCustomers ;
string sql = "select customerid, firstname, middlename, lastname, companyname, emailaddress, phone, modifieddate from saleslt.customer";
DbCommand cmd = db.GetSqlStringCommand(sql);
IDataReader dr = db.ExecuteReader(cmd);
lstCustomers = new List<Domain.Customer>();
while (dr.Read())
{
Domain.Customer c = new Domain.Customer();
c.CompanyName = dr["companyname"].ToString();
c.CustomerId = Convert.ToInt32(dr["customerid"]);
c.EmailAddress = dr["emailaddress"].ToString();
c.FirstName = dr["firstname"].ToString();
c.LastName = dr["lastname"].ToString();
c.MiddleName = dr["middlename"].ToString();
c.ModifiedDate = Convert.ToDateTime(dr["modifieddate"]);
c.Phone = dr["phone"].ToString();
lstCustomers.Add(c);
}
if (!dr.IsClosed)
dr.Close();
return lstCustomers;
}
Listagem 03
E para testar o código acima vamos escrever as seguintes linhas no nosso método Main no Projeto Console, logo abaixo da última linha já existente do nosso teste anterior:
IList<Domain.Customer> lstCustomer = dalCustomer.GetCustomers();
foreach (Domain.Customer customer in lstCustomer)
{
System.Console.WriteLine(customer.CustomerId.ToString() + " - " + customer.FirstName.ToString() + " " + customer.LastName.ToString());
}
System.Console.ReadKey();
Listagem 04
O código acima chama o método da DAL e retorna uma coleção de Clientes. Se aparecerem várias centenas de Clientes, está certo!
O código escrito até agora. Será que é possível aplicar um Refactoring para melhorar algo? Bom, tem algo bem evidente, uma duplicação de código, os dois métodos fazem a leitura do DataReader e populam um objeto Customer, e um deles ele adiciona a uma lista de objetos já que o retorno são vários. Podemos centralizar esse código para que quando for preciso adicionar uma nova Propriedade a classe Customer seja preciso alterar somente em um lugar, então vou criar o método AdaptToList que irá receber um DataReader e retornar uma lista tipada de Customer:
public IList<Domain.Customer> AdaptToList(IDataReader dr)
{
}
Listagem 05
O código da listagem 05 está praticamente pronto. Mova o código que se encontra na listagem 03, da linha 11 até 27 para dentro do método AdaptToList, e adicione a última linha, conforme a listagem 06! E repare que na inicialização da coleção Customer a declaração estava em outra linha, então acrescente o tipo antes.
public IList<Domain.Customer> AdaptToList(IDataReader dr)
{
List<Domain.Customer> lstCustomers = new List<Domain.Customer>();
while (dr.Read())
{
Domain.Customer c = new Domain.Customer();
c.CompanyName = dr["companyname"].ToString();
c.CustomerId = Convert.ToInt32(dr["customerid"]);
c.EmailAddress = dr["emailaddress"].ToString();
c.FirstName = dr["firstname"].ToString();
c.LastName = dr["lastname"].ToString();
c.MiddleName = dr["middlename"].ToString();
c.ModifiedDate = Convert.ToDateTime(dr["modifieddate"]);
c.Phone = dr["phone"].ToString();
lstCustomers.Add(c);
}
return lstCustomers;
}
Listagem 06
Para funcionar só é preciso chamar o método AdaptToList no método GetCustomers, também alterei para que a lstCustomer fosse baseada em uma IList<T>, conforme a listagem abaixo:
public IList<Domain.Customer> GetCustomers()
{
IList<Domain.Customer> lstCustomers = null;
string sql = "select customerid, firstname, middlename, lastname, companyname, emailaddress, phone, modifieddate from saleslt.customer";
DbCommand cmd = db.GetSqlStringCommand(sql);
IDataReader dr = db.ExecuteReader(cmd);
lstCustomers = AdaptToList(dr);
if (!dr.IsClosed)
dr.Close();
return lstCustomers;
}
Listagem 07
Mais um pequeno passo, e mais um teste! Se rodou e continuou funcionando é por que o seu Refactoring foi bem feito! O que falta? Bem, se AdaptToList foi contruído para economizar código devemos usá-lo também no método GetCustomerById, mas nesse último método ele retorna apenas um Customer e não uma lista. Podemos então usar um artifício: pegar somente o primeiro item da lista para retornar, conforme listagem abaixo:
public Domain.Customer GetCustomerById(int customerId)
{
IList<Domain.Customer> lstCustomers = null;
string sql = "select customerid, firstname, middlename, lastname, companyname, emailaddress, phone, modifieddate from saleslt.customer where customerid = @customerid";
DbCommand cmd = db.GetSqlStringCommand(sql);
db.AddInParameter(cmd, "customerid", DbType.Int32, customerId);
IDataReader dr = db.ExecuteReader(cmd);
lstCustomers = AdaptToList(dr);
if (!dr.IsClosed)
dr.Close();
�
return ((lstCustomers != null) || (lstCustomers[0] != null)) ? lstCustomers[0] : null;
}
Listagem 08
Na listagem 08 está o método GetCustomerById como deve ficar, a mudança do List<Domain.Customer> para IList<Domain.Customer>, a chamada do método AdaptToList e o pulo do gato que é o uso do operador ternário para retornar somente o primeiro item da coleção, ou seja um objeto Customer, ou um null, não vamos discutir agora se retornar nulo é uma boa opção ou não!
Vou finalizar o post por aqui, para não ficar muito grande, resumindo o que fizemos aqui: Refactoring!
Melhoramos o nosso código, parece que não é necessário fazer isso agora, mas sempre que possível é interessante a fazer, no caso criamos uma classe para cuidar inicialmente da conexão com o banco de dados e como criamos outro método que populava a classe Customer criamos um método para cuidar especificamente disso.
O código deste post encontra-se no Change Set 35524, dúvidas e sugestões por favor comentem!
Referências:


