DataSets vs. Collections

Dino Esposito escreveu uma matéria bastante interessante na coluna Cutting Edge da revista MSDN Magazine deste mes, chamada “DataSets vs. Collections“, onde ele mostra os prós e contras de cada um deles. Gostei bastante de um parágrafo onde ele cita o uso/cenário dos Datasets: 🙂

“Both DataSets and custom classes don’t limit what you can do in any way, and both can be used to accomplish the same aims. That said, DataSets are fantastic tools for prototyping applications and represent excellent solutions for building systems in a kind of emergency—a limited budget, an approaching deadline, or a short application lifetime. For relatively simple applications, custom entities add a perhaps unnecessary level of complexity. In this case, I suggest that you seriously consider using DataSets.”

Unificando o Gerenciamento de Credenciais

Juval Lowy escreveu um artigo muito interessante explicando como unificar o gerenciamento de credenciais entre aplicações Windows Forms e ASP.NET neste endereço.

Como sabem, o .NET Framework 2.0 nos fornece uma infraestrutra para gerenciamento de credenciais/usuários através do que chamamos de MembershipProvider. O artigo define um Web Service para poder “compartilhar” com aplicações Windows Forms esta mesma infraestrutura. Abaixo o design da solução:

default Keyword

Algo bastante interessante que está disponível na versão 2.0 do C# é a keyword default. Ela é utilizada em classes genéricas para inicializar um tipo qualquer, ou seja, como em C# temos obrigatoriamente que definir um valor default para qualquer tipo, esta vem para suprir esta necessidade.

Em casos de Reference Type, é retornado um valor nulo. Já em casos de valores numéricos, 0 é retornado. Quando o tipo é uma estrutura, é retornado para cada membro desta, 0 ou nulo, dependendo do tipo de cada um. Abaixo um exemplo do uso desta funcionalidade:

     public class Lista <T> where T : IComparable{
          public void Add(T item){
               T temp = default(T);
               // ….
          }
     }

foreach – C#

Nesta nova versão da linguagem C#, temos novas formas de percorrermos Arrays. Nas versões anteriores (1.x), faríamos algo do tipo:

     string[] categorias = new string[] {“ASP.NET”, “VB.NET”, “C#”}
     foreach(string s in categorias)
          Console.WriteLine(s);

Agora temos duas opções para isso: utilizarmos o método ForEach da classe Array com um método auxiliar para processar o que queremos fazer com cada elemento do Array ou mesmo utilizando métodos anonimos:

     [ Usando um método auxiliar ]
     string[] categorias = new string[] {“ASP.NET”, “VB.NET”, “C#”}
     Array.ForEach(categorias, Escrever);
     //…
     private void Escrever(string s){
          Console.WriteLine(s);
     }

     [ Usando um método anonimo ]
     string[] categorias = new string[] {“ASP.NET”, “VB.NET”, “C#”}
     Array.ForEach(categorias, delegate(string s)
     {
          Console.WriteLine(s);
     });

Eu custei a entender esses métodos anonimos, e particularmente agora, um pouco mais acostumado com eles acho mais prático que a criação de um método auxiliar.

Retornando DataReaders através de Funções

Há casos onde é retornado ao Cliente pela camada de acesso à dados objetos do tipo DataReader. Seja ele qual for (OleDb, Sql, Odbc ou Oracle) é um tanto perigoso. Como já sabemos, para que o DataReader resgate os valores da Fonte de Dados, é necessário ter uma conexão ativa com o mesmo, caso contrário, não conseguirá.

Neste cenário, o que realmente é comum são funções que retornam DataReaders, utilizando a opção CloseConnection do Enumerador CommandBehavior, que faz sentido, deixando a cargo do DataReader fechar a conexão com a Fonte de Dados assim que finalizar seu trabalho. Com isso, temos dois problemas:

• Ao retornar XXXDataReader para a camada de negócios, ou mesmo para a camada de apresentação, gera-se uma dependência do Provider, ou seja, se futuramente pretender migrar de Base Dados/Provider terá que rescrever tudo;
 
• Quando não se utiliza alguma DAL (Data Access Layer) para executar comandos e gerir as conexões com a Fonte de Dados, fica a cargo* do Cliente fechar o DataReader, pois sabemos que temos uma conexão o servindo qual será/deverá ser fechada quando o DataReader for fechado e, em muitos casos por algum descuido, pode não ser fechado o DataReader, e consequentemente “estourando” o pool de conexões com a Fonte de Dados, impedindo assim que se abram novas conexões.

* DALs também permitem que se retarde o fechamento da conexão com a Base de Dados, o qual deve se feita explicitamente.

DMAB – Data Mapping Application Block

Foi lançando uma versão preview do DMAB – Data Mapping Application Block – que como o próprio nome diz, é utilizado para mapear os dados da DB para objetos da nossa aplicação.

Baixei o código para dar uma olhada superficial, e pude ver que o mapeamento só é feito para objetos do tipo Dataset com o uso de Stored Procedures, não permitindo assim trabalharmos com os nossos próprios objetos e com a geração automática de códigos SQL.

List.ConvertAll

Estive olhando os Generics e seus métodos, e teve um deles que me chamou a atenção. O método que me refiro é o List.ConvertAll<T>, que pelo que pude ver, ele retorna uma nova lista, podendo inclusive ser de um outro tipo. Este método por sua vez, recebe em seu parametro um delegate que será responsável por fazer as devidas conversões.

Como estamos utilizando C#, podemos ao invés de criar um delegate e um método auxiliar para esta conversão, criarmos um método anonimo, que além de mais elegante, escreve-se menos código. Eis aqui um exemplo simples da utilização deste método:

     List<Usuario> usuarios = new List<Usuario>();
     usuarios.Add(new Usuario(“Nome Usuario 1”));
     usuarios.Add(new Usuario(“Nome Usuario 2”));
     usuarios.Add(new Usuario(“Nome Usuario 3”));

     List<string> nomes = usuarios.ConvertAll<string>(delegate(Usuario u)
     {
          return u.Nome;
     });

     foreach(string s in nomes){
          Console.WriteLine(s);
     }

Como vemos, através do método ConvertAll, específicamos o tipo que será retornado e através do método anonimo, que recebe um objeto do tipo Usuario e através deste, recupera o valor da propriedade Nome para que a mesma seja adicionada na lista de strings denominada “nomes”.

Eu comecei a fazer os testes em um projeto em VB.NET, mas infelizmente, o mesmo não tem suporte à métodos anonimos, obrigando-nos a criar o Delegate e também um método auxiliar para que o mesmo retorne o valor a ser recuperado e armazenado nesta nova listagem.

SqlConnection1.Open() – Sim, mas onde?!?

Já não é a primeira vez que vejo isso. Primeiramente em um outro lugar em que trabalhava, me disseram que o correto seria chamar o método Open da XXXConnection fora (antes) do bloco Try, e quando questionei o porque, não souberam me explicar. Vejo isso muitas vezes em fóruns e recentemente li um artigo em uma revista nacional em que o autor fazia isso. Exemplo:

          Dim conn As New SqlConnection(“CONNECTION_STRING”)
          ‘….
          ‘….
          conn.Open()
          Try
               ‘….

Pois bem, e se o servidor SQL Server estiver indisponível ou não existir o que acontecerá? O bloco Catch entrará em ação? Claro que não, pois a linha que gerou o erro, não está “gerenciada” pelo bloco Try e a Exception será atirada ao cliente. Pois bem, e porque será muitas pessoas insistem em dizer que o método acima é a forma correta de abrir a conexão com uma DB? Realmente estou muito curioso!

Outro código falho, é se fizer o seguinte:

          Dim conn As New SqlConnection(“CONNECTION_STRING”)
          ‘….
          ‘….          
          Try
               conn.Open()
               ‘….
               ‘….
               conn.Close()
          Catch ex As Expcetion
               ‘….

Isso também é bastante perigoso, já que se alguma Exception ocorrer depois da abertura da DB e antes do fechamento da mesma, logicamente a conexão com esta DB não fechará, e com isso, em breve o pool de conexões estoura, o que impedirá a sua aplicação e continuar a execução corretamente.

Esse tema também já foi discutido neste post, bem como as formas corretas de abrir e fechar a conexão, mas o fato de que as pessoas insistem em fazer dessa forma, ou seja, de abrir a conexão com a DB antes/fora do bloco Try ainda me deixa bastante confuso, onde não sei e gostaria de saber o que elas pensam a respeito. Será que alguém tem alguma explicação para tal?

DB Pooling

Com a versão 2.0 do .NET Framework, novos métodos foram criados para o gerencimento do pool de conexões para Databases, que até então, não tínhamos acesso. De toda a forma, a pooling ainda é gerido pelo .NET, mas temos agora métodos para poder limpar este pool de conexões.

Esta funcionalidade está disponível para os Providers SqlClient e OracleClient, em dois métodos estáticos:

               * ClearPool(Connection) – Limpa o pool e fecha as conexões ativas para uma conexão específica.
                              [ SqlClient ] [ OracleClient ]

               * ClearAllPools – Limpa o pool e fecha todas as conexões ativas.
                              [ SqlClient ] [ OracleClient ]