ABC App – 04 Exceções

Ia deixar para escrever sobre o assunto depois, mas pra começar bem a semana vou usar o comentário do Tucaz e fazer um Refactoring no código e tratar Execeptions, ou Exceções!

exceção
ex.ce.ção
sf (lat exceptione) 1 Ato ou efeito de excetuar. 2 Desvio de regra, de lei, de princípio ou de ordem. 3 A coisa excetuada; aquilo que se desvia da regra. 4 Prerrogativa, privilégio. 5 Pessoa cujo modo de pensar ou de proceder se afasta do comum e usual. 6 Dir Alegação jurídica, constituindo defesa indireta (difere da contestação, que é defesa direta), pela qual o réu pretende baldar a ação intentada. E. declinatória, Dir: a que visa a declinar a competência do juiz ou tribunal ao qual foi apresentada a demanda. E. dilatória, Dir: a que pretende apenas demorar a demanda. E. peremptória, Dir: a que de todo e definitivamente afasta a demanda.

Conforme a definição acima do Michaelis, exceção é um desvio da regra. Na classe Customer da camada de DAL, quando fazemos acesso ao banco de dados para buscar um ou mais usuários, a EntLib abre uma conexão e executa o comando Select, essa é a regra, mas e quando o banco de dados não esta disponível? Ou se alguém renomeou ou apagou a tabela que esta na nossa query? Isso são exceções, e se não tratarmos, o sistema para de funcionar! Para testar é só desligar o banco de dados (parando o serviço SQL Server Browser) e rodar o nosso software.

Existem várias correntes de pensamento sobre Exceptions

Devemos tratar Exceptions como conexões a banco de dados, mas se um usuário do sistema cadastrar um número inválido de CPF devemos lançar uma Exeception? Devemos criar nossas próprias Execeptions para o nosso sistema? Onde devemos tratar? Muitas perguntas, algumas vão ficar sem respostas no momento, o objetivo desse post será fazer o Refactoring, e o primeiro será na conexão do banco.

Tratamento de Exceptions

No .Net é usado a tríade Try-Catch-Finally, o código passível de erro e que pode vir a lançar uma exceção é colocado no bloco do Try, no Catch é onde se pegam as execeções, e o Finally serve para finalizar alguma operação, limpar alguma variável,  e, muitas vezes,  ele não é usado, mas no nosso primeiro exemplo já faremos uso dele.
Uma regra que devemos sempre atentar é: nunca, NUNCA, colocar um método inteiro dentro de um bloco Try, não há necessidade de colocar criação de variáveis! Teste somente o que pode dar um erro. Além disso,  o código fica mais elegante.

No código da classe Customer da camada de DAL o que pode dar erro? O mais óbvio seria quando o DataReader estivesse sendo carregado, se nessa hora o banco de dados cair o nosso software vai pro espaço! Como no método AdaptToList ainda usamos o DataReader (ele fica aberto para popularmos o objeto Customer)  vamos colocar a linha de ExecuteReader e a de chamada do AdaptToLista dentro do Try. Para fazer isso,  vamos usar o recurso de Refactoring da IDE :  selecione as duas linhas, clique CTRL, e no menu de contexto escolha Surrond With…

Fazendo um Surrond With no código
Fazendo um Surrond With no código

Escolha a opção “tryf”, conforme figura abaixo, repare que este é um atalho para um Snippet, então quando estiver escrevendo código usando este atalho economiza tempo!

Escolha "tryf"
Escolha “tryf”

O código estará dentro do bloco Try, veja outras alterações no código abaixo e em seguida o por quê

[code lang=”csharp”]
public Domain.Customer GetCustomerById(int customerId)
{
IDataReader dr = null;
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);

try
{
dr = db.ExecuteReader(cmd);
lstCustomers = AdaptToList(dr);
}
catch (System.Data.SqlClient.SqlException exsql)
{
throw new Exception(exsql.Message);
}
catch {
}

finally
{
if (!dr.IsClosed)
dr.Close();
}

return ((lstCustomers != null) || (lstCustomers[0] != null)) ? lstCustomers[0] : null;
}
}
[/code]

Listagem 01

Movi a declaração do DataReader para o início do método, pois podemos deixar dentro do Try, já que o Finally não iria enxergar esse objeto e depois é só mover a linha que faz a verificação se o DataReader estiver aberto para dentro do Finally, ocorrendo ou não um erro o Finally sempre é executado e daí ele ficará responsável por fechar o DataReader.
O Tucaz também fez um comentário sobre setar o CommandBehavior para CloseConnection, o que faria a conexão com o banco ser fechada após fecharmos o DataReader, mas a EntLib cuida disso pra gente! Veja o código fonte, e essa é a parte legal de usar essa biblioteca da MS, pois com o acesso ao fonte vemos as boas práticas sendo aplicadas, aquelas que se encontram no Guia de Acesso a Dados.
O Catch vazio vai pegar todo o tipo de exceção, então é bom especializarmos o Catch, por isso eu também coloquei para pegar a SQLException.

Aplique esse Refactoring no método GetCustomers() também.

OK… E vamos fazer isso também quando é criado o Database na classe BaseDAL, pois se o banco de dados estiver fora do ar vai dar um erro. É só executar os mesmos passos em cima da única linha que temos no único construtor da classe. E vai ficar assim:

[code lang=”csharp”]
public abstract class BaseDAL
{
public EntLib.Database db { get; set; }

public BaseDAL()
{
try
{
db = EntLib.DatabaseFactory.CreateDatabase(“Connection String”);
}
catch (Exception ex)
{
throw ex;
}
finally
{

}
}
}
[/code]

Listagem 02
E agora temos que exibir esses erros lá na nossa interface de usuário, aqui vai o código de como vai ficar:

[code lang=”csharp”]

namespace ABCApp.Console
{
class Program
{
static void Main(string[] args)
{

ABCApp.Domain.Customer c;

ABCApp.DAL.Customer dalCustomer = new ABCApp.DAL.Customer();

try
{
c = dalCustomer.GetCustomerById(1);
System.Console.WriteLine(c.CustomerId.ToString() + ” – ” + c.FirstName.ToString() + ” ” + c.LastName.ToString());
}
catch (Exception ex)
{
System.Console.WriteLine(ex.Message);
}

System.Console.ReadKey();

try
{
IList<Domain.Customer> lstCustomer = dalCustomer.GetCustomers();

foreach (Domain.Customer customer in lstCustomer)
{
System.Console.WriteLine(customer.CustomerId.ToString() + ” – ” + customer.FirstName.ToString() + ” ” + customer.LastName.ToString());
}
}
catch (Exception ex)
{
System.Console.WriteLine(ex.Message);
}

System.Console.ReadKey();
}
}
}
[/code]

Vamos testar? Para isso desligue o seu SQL Server! Vá no menu Iniciar > Microsoft SQL Server 2008 > Configuration Tools > SQL Server Configuration Manager, entre na ferramenta.  Pare o serviço SQL Server Browser, daí não deve ser possível conectar na base. Rodando a aplicação devem ser impressas mensagens de erro onde deveriam aparecer dados!

Bom… Isso foi só para começarmos a colocar um controle sobre os locais onde podem ocorrer Exceptions, esse não é o mundo perfeito, e nem será a última vez que irei falar sobre o tema. Mas por que não ler alguns links sobre isso? Aí vai:

http://unplugged.giggio.net/unplugged/post/Como-tratar-erros.aspx

http://msdn.microsoft.com/en-us/library/dd203116.aspx (Esse é sobre o bloco de Exceptions da EntLib, mais pra frente vamos usar ele)

http://www.developerfusion.com/article/5250/exceptions-and-performance-in-net/ (discute a questão de performance ao lançar exceções)

http://yoda.arachsys.com/csharp/exceptions2.html (também sobre performance)

http://www.artima.com/interfacedesign/AbnormalConditions.html

http://apparch.codeplex.com/ (aqui tem um vídeo sobre o tema)

Fico por aqui… Na quinta-feira post sobre objetos anêmicos…

O código deste post esta no Change Set 36605.

Padrão MVC arquitetura em camadas

(Nota: publiquei esse post inicialmente aqui, como movi o meu blog , e recebi pedidos para comentar mais a respeito, estou revisando e postando aqui para continuar a série de posts do blog antigo)

Estou desenvolvendo um novo sistema baseado na arquitetura MVC, ou melhor, pensei que estava.

Em 2007, participei da integração de um sistema legado ASP.Net com SAP que estava sendo implantado. O sistema já estava construído e basicamente deveríamos integrá-lo ao SAP usando Web Services disponibilizados através do serviço XI da SAP. Ou seja, ao invés de continuar buscando os dados no banco de dados Oracle, iríamos agora buscar no SAP, através de Web Services.

O sistema usava a arquitetura MVC. Usava? Bom, eu tinha uma DLL onde ficavam as classes Model, outra de Controller, tinha a interface gráfica em ASP.Net, mas eram somente camadas… Opa, mas MVC não são camadas?

Esse foi o meu primeiro contato com MVC e a partir daí comecei a estudar mais sobre padrões e arquitetura, e lógico vi que era hora de me aprofundar em Orientação a Objetos… Sim isso mesmo, aprofundar.

Eu vim do VB6 (por favor, VB6 é sim uma linguagem de gente grande!), quando comecei a programar o ADO estava sendo lançado, praticamente. Logo em seguida tive contato com a arquitetura Win DNA (Windows Distributed interNet Applications Architecture), como o link diz é um nome marketeiro para tecnologias que já existiam mas foram agrupadas em uma arquitetura (COM, COM+, antigo MTS; ADO, ActiveX, ASP). Na época a minha bíblia era o livro Mary Kirtland, posteriormente li também o livro do Fábio Câmara.

E foi aí que surgiu para mim o conceito de camadas, dividir para conquistar, já que na época tínhamos o DLL Hell, era muito bom você criar pequenos componentes que sofreriam manutenção em separado, e nada melhor do que juntar esses componentes por funcionalidades! Assim os componentes usados para a interface gráfica ficavam juntos, o de acesso a dados ficavam em outro, o que diminuía a possibilidade de dar um problemão quando alguma coisa sofria manutenção, eu disse diminuía…

Daí pra frente eu só desenvolvia em camadas, camadas lógicas, pois na verdade o software ficava instalado todo na máquina cliente, ou seja, eram instaladas várias DLL’s, mas todas no mesmo lugar. Algum projeto saiu usando o COM+, aí tinhamos Tiers, componentes usados em interface gráfica ficava na máquina cliente e compoentes de negócio e banco de dados ficavam no servidor.

Mas onde entra o MVC aí? Aí é que está… Não entra!! O MVC não é sinônimo de desenvolvimento em camadas! Nem em tiers! O MVC é um padrão de arquitetura, e ele é baseado no comportamento dos objetos. Sim, comportamento!! E mais, o MVC é padrão para interface gráfica, e não para todo o sistema.
Muitos de nós, principalmente que viemos do VB6, Win DNA, …; começamos desenvolvendo em OO criando classes que tem os atributos como os RecordSets do ADO, ou seja somente dados! Mas um objeto por definição possui comportamento. Então não adianta criar uma classe de dados, como se fosse um RecordSet, uma classe de serviço como se fosse uma classe do VB6 (que sim, antes que alguém fale, não é orientado a objeto, porém chegava perto…), e ficar passeando pelas camadas, que isso é MVC. Aliás nem OO é, pois você não estará usando comportamentos dos objetos.

Não vou chover no molhado explicando isso aqui, o Phillip Calçado Shoes já escreveu um artigo muito bom sobre isso, então usando um dos princípio de OO que é a reusabilidade , para saber mais leia os artigos MVC e Camadas e Evitando VO’s e BO’s, leia também as referências e acompanhe o blog dele! 😀

Na edição 46 da .Net Magazine o Rodrigo Sendin escreveu um artigo sobre MVC, porém quem leu o artigo e ler os artigos do Phillip Calçado vai entender que a crítica do Rodrigo esta errada quanto ao padrão MVC.

Bom se eu não vou explicar o que é MVC, nem camadas, nem BO ou VO, então pra que este post? Como eu disse estava desenvolvendo um projeto pensando estar usando MVC, no momento na versão 1.0 ele irá sair usando BO’s, trafegando pelas camadas, etc… Mas estou montando a arquitetura da versão 2.0 em MVC, não vou usar nenhum framework, pelo menos por enquanto.

No próximo post (espero mesmo começar aqui neste novo endereço do blog em breve) vou começar uma série de artigos compartilhando minha experiência, principalmente com uso de objetos POCO, pois percebo que no Brasil o uso de DataSet’s é abusivo, logicamente para pequenos projetos é uma boa solução mas para projetos médios, ou com muitos acessos ao banco de dados o peso começa aumentar. E também vou dar um foco no acesso a dados. Vou publicar o código acho que no CodePlex (o código esta aqui, ou melhor, estará :D), daí é só baixar o código para estudar ou começar outro projeto em cima. Quem quiser se unir a empreitada é só entrar em contato.

Espero que acompanhem, comentem, entrem em contato para trocarmos idéias.