Retorno de DataReaders – Solução Elegante

Todos sabemos que retornar DataReaders através de funções é um tanto quanto perigoso, pois o DataReader exige uma conexão ativa com a base de dados e delega ao consumidor do método fechar a conexão com o mesmo explicitamente. Apesar de perigoso, há um cenário onde retornar DataReaders é muito comum, ou seja, quando estavamos desenvolvendo uma DAL, um dos principais métodos é o ExecuteReader, a menos que voce somente trabalhe com Datasets 😛

Keith Brown dá uma solução muito elegante para continuar retornando DataReaders através de funções, porém a responsabilidade em fechar o mesmo, fica dentro da própria classe, evitando assim que o DataReader fique aberto, prejudicando a aplicação. A solução é a utilização de um delegate que, em sua assinatura, recebe um parametro do tipo XXXDataReader. Se estivermos consumindo a classe a partir do C#, podemos optar por utilizar um método anonimo para as coisas ficarem ainda mais bonitas:

DalHelper dal = new DalHelper(Util.Settings.DbConnectionString);
dal.ExecuteSecureReader(“SELECT Nome FROM Clientes”, CommandType.Text,
    delegate(DbDataReader dr)
    {
        while (dr.Read())
        {
            Response.Write(dr.GetString(0));
        }
    });

Se o consumo da classe for no VB.NET, voce consegue ter todos os beneficios com relação ao fechamento da conexão, mas precisará criar uma função a parte para tratar o resultado. Com certeza, o benefício vale mais do que 3 ou 4 linhas de código a mais. 😉

Encoder Fallbacks

Quando utilizamos algum codificador que não consegue codificar ou decodificar algum caracter, ele coloca um ponto de interrogação “?” para indicar que o codificador corrente não é capaz de “traduzí-lo”.

A classe Encoding fornece dois métodos chamados GetEncoder e GetDecoder, que retornam os objetos responsáveis por codificar e decodificar as strings, respectivamente. Ambas as classes possuem uma propriedade chamada FallBack. Essa propriedade recebe um objeto do tipo EncoderFallback ou DecoderFallback que representam uma ação que será executada quando um caracter ou um byte não puder ser convertido.

As classes EncoderFallback e DecoderFallback são utilizadas na codificação e decodificação, respectivamente. A primeira delas é a classe base para todos os fallbacks de codificação e, a segunda, a classe base para todos os fallbacks de decodificação. Basicamente, para ambos os casos, temos dois tipos de fallbacks

  • Fallbacks de Substituição: Quando um caracter ou byte não consegue ser “traduzido”, ele substitui o mesmo por um caracter que podemos determinar. Por padrão, o ponto de interrogação “?” é utilizado.
    • EncoderReplacementFallback: Fallback que é invocado quando um caracter que puder ser convertido em uma seqüencia de bytes, convertendo o caracter para um valor pré-definido.
    • DecoderReplacementFallback: Fallback que é invocado quando uma seqüencia de bytes não puder ser convertida em um caracter, convertendo o byte para um valor pré-definido.
  • Fallbacks de Excessões: Quando um caracter ou byte não consegue ser “traduzido”, ele atira uma excessão do tipo EncoderFallbackException ou DecoderFallbackException, dependendo da operação que estamos tentando realizar.
    • EncoderExceptionFallback: Fallback que é invocado quando um caracter que puder ser convertido em uma seqüencia de bytes, atirando uma excessão do tipo EncoderFallbackException.
    • DecoderExceptionFallback: Fallback que é invocado quando uma seqüencia de bytes não puder ser convertida em um caracter, atirando uma excessão do tipo DecoderFallbackException.

O código abaixo exemplifica a utilização de um dos fallbacks acima:

using System.Text;
//….
Encoder encoder = Encoding.ASCII.GetEncoder();
encoder.Fallback = new EncoderReplacementFallback(“*”);
char[] chars = “ãBC”.ToCharArray();
Byte[] buffer = new byte[chars.Length];
encoder.GetBytes(chars, 0, chars.Length, buffer, 0, true);
Console.WriteLine(Encoding.ASCII.GetString(buffer));

O resultado ao executar esse código é: *BC. O “ã” é substituído pelo “*” porque esse caracter não está contemplado no padrão ASCII e, conseqüentemente, não consegue ser “traduzido”. A utilização dos fallbacks são muito úteis quando voce quer customizar a leitura ou gravação de streams. A utilização de fallbacks de excessões são as vezes mais utilizadas quando a leitura dos caracteres devem ser precisa.

Worker Process Accounts e Certificados

Quando configuramos um certificado dentro de uma aplicação IIS, é necessário concedermos direitos de leitura a chave privada do certificado em questão para as contas dos worker process do IIS. Via Alexander Strauss eu encontrei a forma de fazer isso:

  1. Através do utilitário FindPrivateKey, conseguimos identificar a chave privada, para que seja possível encontrá-la e, em seguida, conceder os direitos a mesma. Se não tiver o Microsoft SDK for .NET Framework 3.0 instalado, então voce pode baixar os exemplos de WCF da Microsoft que, dentro dele, existe um projeto chamado FindPrivateKey.sln, que é justamente o projeto que gera esse utilitário.
  2. Depois de encontrado a chave, voce pode conceder os direitos através do utilitário cacls.exe (como é mostrado no post do Alexander) ou via Windows Explorer >> Security.

Geralmente, alguns possíveis erros que podem acontecer quando voce executa um serviço WCF e o worker process não tem permissão de leitura a uma determinada chave privada. Pude detectar dois deles:

  • System.ArgumentException was unhandled Message=”The certificate ‘CN=Chave’ must have a private key that is capable of key exchange.  The process must have access rights for the private key.” Source=”System.ServiceModel”
  • CryptographicException: Keyset does not exist

É importante lembrar que se voce alterar as contas padrões no IIS para o worker process, é necessário conceder o direito de leitura a chave privada do certificado para o usuário ali determinado.

Pool de Objetos

Na empresa onde trabalho, desenvolvemos alguns objetos que são muito custosos durante a sua criação. São checagens, a nível de segurança e também a dados dentro de um determinado banco de dados. Quando inciamos a fase de testes, notamos que o construtor e alguns trabalhos que fazemos durante os métodos e propriedades estava levando muito tempo para serem executados e, na maioria das vezes, eles fazem sempre as mesmas validações.

Para contornar a situação, optamos por implementar uma espécie de pool de objetos. Depois de muito ler e efetuarmos testes, chegamos a uma forma que, até então, parece-me trabalhar bem. Essa técnica trata-se da criação de um membro privado estático, do tipo Stack<T> que será o responsável por armazenar os objetos dentro do pool. Logo depois da criação, via construtor, o objeto é automaticamente adicionado a Stack. Como não poderia deixar de ser, mais dois métodos estáticos forem criados: um para extrair o objeto do pool e outro para finalizar o pool, caso não precisar mais dele na aplicação. Além disso, optamos também por criar uma propriedade que irá determinar se aquele objeto deve ou não ser armazenado no pool ou se deve ser descartado diretamente.

public class Order
{
    private static Stack<Order> _orderPool = new Stack<Order>();

    public Order()
    {
        _orderPool.Push(this);
    }

    public static Order GetOrderFromPool()
    {
        return _orderPool.Pop();
    }

    public static void ClosePool()
    {
        _orderPool = null;
    }

    protected virtual bool CanBePooled
    {
        get
        {
            return true;
        }
    }

    ~Order()
    {
        if (_orderPool != null && this.CanBePooled)
        {
            GC.ReRegisterForFinalize(this);
            _orderPool.Push(this);
        }
    }
}

O único código que merece um comentário adicional é com relação ao destrutor da classe Order. Dentro dele verificamos se o pool não está nulo (isso quer dizer que a aplicação não precisa mais dele) e, além disso, a propriedade CanBePooled deve estar definida como True, o que indica que o objeto pode ser adicionado ao pool. Se essas condições forem atendidas, chamamos o método ReRegisterForFinalize da classe GC, passando a instancia corrente da classe para que o mesma seja ressucitada e, como podemos notar, a instancia é devolvida para o pool via método Push. Thanks JeffreyR 😉