Propagando Transações em Métodos Assíncronos

Desde a versão 2.0 do .NET Framework existe um assembly chamado System.Transactions.dll. Dentro deste assembly há diversos tipos para trabalharmos com transações dentro de aplicações .NET. Basicamente passamos a ter o controle, através de uma linguagem .NET, de um ambiente transacionado, em que podemos delimitar o escopo e decidir quando e onde queremos efetivar (commit) ou desfazer (rollback) as alterações .

Através deste mesmo conjunto de classes, temos a possibilidade de alistar vários tipos de recursos, e entre eles temos base de dados relacionais, filas de mensagens (message queue) e até mesmo, com algum trabalho, recursos voláteis. Além disso, este mecanismo é inteligente o bastante para determinar quando ele precisa apenas de uma transação local (quando envolve apenas um resource manager), e quando há mais que um envolvido, ele é capaz de escalar para uma transação distribuída de forma automática.

Apesar de funcionar bem, alguimas complicações começam a aparecer quando estamos trabalhando com aplicações assíncronas, mas que compartilham do mesmo escopo transacionado. O que quero dizer aqui é que uma vez que o escopo está criado (TransactionScope), se envolvermos chamadas à outros códigos de forma assíncrona, esperando que a transação seja propagada para essas outras threads, teremos um comportamento não desejado. Isso se deve ao fato de que, por padrão, a transação não é (automaticamente) propagada para essas outras threads que estão fazendo um trabalho complementar ao principal.

Para resolvermos este problema até então, devemos recorrer à classe DependentTransaction. Como o próprio nome sugere, esta classe é um clone da transação que rege o ambiente criado pelo TransactionScope, e garante que escopo principal não possa ser concluído antes que todos os trabalhos que estão sendo executados paralelamente estejam finalizados. A criação desta classe se dá através do método DependentClone da classe Transaction, que como parâmetro recebe uma das opções expostas pelo enumerador DependentCloneOption. No exemplo abaixo utilizaremos a opção BlockCommitUntilComplete, que garantirá que que transação não seja efetivada até que o trabalho dentro do método Metodo1 seja finalizado. O que sinaliza ao coordernador que o trabalho assíncrono foi concluído é a chamada para o método Complete da classe DependentTransaction.

private static void Executar()
{
    using (var scope = new TransactionScope())
    {
        LogTransactionInfo(“Main”);

        ThreadPool.QueueUserWorkItem(
            Metodo1,
            Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete));

        scope.Complete();
    }
}

private static void Metodo1(object root)
{
    using (var dependentTransaction = root as DependentTransaction)
    {
        using (var scope = new TransactionScope(dependentTransaction))
        {
            LogTransactionInfo(“Metodo1”);

            scope.Complete();
        }

        dependentTransaction.Complete();
    }
}

Se avaliarmos o log do identificador da transação, veremos que ambos possuem o mesmo ID:

Main: 5bb899d4-3428-42ce-9c45-d498900be040:1
Metodo1: 5bb899d4-3428-42ce-9c45-d498900be040:1

Como o .NET Framework em conjunto com as linguagens estão tentando tornar a construção de aplicações assíncronas mais simples, a Microsoft incluiu na versão 4.5.1 do .NET Framework um novo construtor na classe TransactionScope que aceita uma das duas opções expostas pelo enumerador TransactionScopeAsyncFlowOption. A opção Enabled que é utilizada abaixo indica ao .NET que o escopo transacionado deve ser propagado para os métodos assíncronos que são invocados dentro dele. Isso facilita a codificação, pois podemos tornar o código mais legível, sem a necessidade de ficar controlando detalhes de infraestrutura, e isso pode ser comprovado através do exemplo abaixo. O enumerador TransactionScopeAsyncFlowOption também possui a opção Supress, que é a configuração padrão e é indica que o contexto transacionado não seja propagado.

private async static Task Executar()
{
    using(var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
    {
        LogTransactionInfo(“Main”);

        await Metodo1();

        scope.Complete();
    }
}

private async static Task Metodo1()
{
    LogTransactionInfo(“Metodo1”);

    await Task.Delay(100);
}

E, finalmente, como já era de se esperar, os IDs das transações são idênticos, tanto na thread principal quanto na worker thread:

Main: d7f86e15-8bf8-4472-aeb7-be661a2c5703:1
Metodo1: d7f86e15-8bf8-4472-aeb7-be661a2c5703:1

Postergando o Disparo de Exceções

Utilizamos o bloco try/catch para conseguir tratar erro onde ele acontece, ou seja, envolvemos no bloco try o trecho de código que pode dar erro, e se ele acontecer, o bloco catch, opcionalmente, pode capturar o erro e o tratar, dando uma mensagem customizada, realizando o log, etc.

Na grande maioria das vezes quando trabalhamos na construção de uma biblioteca, não devemos tratar o erro no local, pois o ideal é deixar que ele seja disparado e propagado, para que assim o consumidor tenha a chance de saber o porque aquele erro aconteceu. E, se desejar interceptar a exceção dentro da biblioteca, é necessário redisparar a exceção preservando a stack trace, que é onde é armazenado todo o caminho (classes e métodos) até onde o erro de fato ocorreu. O código abaixo exibe como fazer isso:

try
{
    //Algum Código
}
catch (Exception ex)
{
    //Log, Tratamento

    throw;
}

Quando o throw for executado, o .NET dispara a exceção que ocorreu mantendo toda a stack trace, o que é essencial para que o consumidor possa entender o que houve. Só que se quisermos executar mais algum código a partir desta linha e postergar o disparo da exceção, não é possível, pois essa keyword tem uma tratativa especial pelo .NET Framework e aborta a execução das linhas que estão na sequência.

Isso inclusive era uma dificuldade do time do .NET Framework para conseguir fazer com que as exceções que ocorriam dentro dos métodos assíncronos fossem propagados sem perder as informações (stack trace). Para resolver isso, foi criado uma classe chamada ExceptionDispatchInfo (namespace System.Runtime.ExceptionServices), que através do método estático Capture, recebe como parâmetro e armazena a exceção que ocorreu, e mais tarde, quando desejar, podemos redispará-la através do método Throw, conforme é mostrado abaixo:

private static string LerConteudo(string nomeDoArquivo)
{
    var conteudo = string.Empty;
    ExceptionDispatchInfo erro = null;

    try
    {
        conteudo = File.ReadAllText(nomeDoArquivo);
    }
    catch (Exception ex)
    {
        erro = ExceptionDispatchInfo.Capture(ex);
    }

    //Algum outro código aqui

    if (erro != null) erro.Throw();

    return conteudo;
}

Unhandled Exception: System.IO.FileNotFoundException: Could not find file ‘C:UsersIsraelDesktopConsoleApplication1ConsoleApplication1binDebugTeste.txt’.
   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
   at System.IO.StreamReader..ctor(String path, Encoding encoding, Boolean detectEncodingFromByteOrderMarks, Int32 bufferSize, Boolean checkHost)
   at System.IO.File.InternalReadAllText(String path, Encoding encoding, Boolean checkHost)
   at System.IO.File.ReadAllText(String path)
   at ConsoleApplication1.Program.LerConteudo(String nomeDoArquivo) in c:UsersIsraelDesktopConsoleApplication1ConsoleApplication1Program.cs:line 27
— End of stack trace from previous location where exception was thrown —
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at ConsoleApplication1.Program.LerConteudo(String nomeDoArquivo) in c:UsersIsraelDesktopConsoleApplication1ConsoleApplication1Program.cs:line 34
   at ConsoleApplication1.Program.Main(String[] args) in c:UsersIsraelDesktopConsoleApplication1ConsoleApplication1Program.cs:line 15

Cultura padrão para AppDomain

O idioma configurado na minha máquina (através do Painel de Controle) é o pt-BR. Ao rodar uma aplicação .NET, por padrão, a mesma é configurada com a cultura que está definida lá, e sendo assim, todas as threads que rodam na aplicação seguirão a mesma cultura. O código abaixo ilustra o código de exemplo:

static void Main(string[] args)
{
    var data = DateTime.Now;
    var valor = 1982.81M;

    ExibirNaMesmaThread(data, valor);
    ExibirEmOutraThread(data, valor);
}

static void ExibirNaMesmaThread(DateTime data, decimal valor)
{
    Console.WriteLine(data);
    Console.WriteLine(“{0:C2}”, valor);
}

static async void ExibirEmOutraThread(DateTime data, decimal valor)
{
    await Task.Delay(1000);

    Console.WriteLine(data);
    Console.WriteLine(“{0:C2}”, valor);
}

O resultado ao rodar este código será:

22/09/2013 16:33:34
R$ 1.982,81

22/09/2013 16:33:34
R$ 1.982,81

A data e o valor decimal estão no formato brasileiro, assim como era de se esperar. Em aplicações onde queremos determinar e/ou fixar a cultura padrão, recorremos à propriedade CurrentCulture da classe Thread para isso, e geralmente fazemos a configuração na inicialização da aplicação. Só que essa configuração somente é válida quando estamos em uma aplicação que trabalha apenas com uma única thread. Se desejarmos configurar a cultura explicitamente, fazemos o seguinte:

var cultura = new CultureInfo(“en-US”);

Thread.CurrentThread.CurrentCulture = cultura;
Thread.CurrentThread.CurrentUICulture = cultura;

Só que ao rodar a aplicação, para nossa surpresa, teremos:

9/22/2013 4:37:58 PM
$1,982.81

22/09/2013 16:37:58
R$ 1.982,81

A primeira exibição mostra os valores no padrão americano, pois está rodando sobre na mesma thread em que configuramos a cultura. Já o método que roda assincronamente e, consequentemente, em outra thread, continua exibindo os valores no formato brasileiro, pois a configuração que fizemos afetou somente a thread corrente, e Task que foi criada pelo .NET para executar o método ExibirEmOutraThread continua utilizando no formato brasileiro.

A partir do .NET Framework 4.5, a classe CultureInfo passa a ter duas novas propriedades estáticas chamadas: DefaultThreadCurrentCulture e DefaultThreadCurrentUICulture, que determinam a cultura padrão para todas as threads que rodam dentro daquele AppDomain. Se optarmos por ela ao invés daquelas propriedades expostas pela classe Thread, teremos ambos os valores formatados igualmente para todos os métodos dentro da aplicação, independentemente de qual thread eles estejam rodando.

var cultura = new CultureInfo(“en-US”);

CultureInfo.DefaultThreadCurrentCulture = cultura;
CultureInfo.DefaultThreadCurrentUICulture = cultura;

9/22/2013 4:43:19 PM
$1,982.81

9/22/2013 4:43:19 PM
$1,982.81

HttpClient na Portable Class Library

É comum termos a necessidade de criar uma biblioteca com algum código que seja compartilhado entre diversos projetos. Para isso, recorremos à uma template de projeto chamada Class Library, em que seu output é um arquivo do tipo DLL, que pode ser referenciado e, consequentemente, utilizado por projetos que queiram utilizar a funcionalidade exportada por ela.

Tudo isso funcionará até o momento em que precisarmos expor uma determinada funcionalidade para diferentes plataformas, algo muito comum hoje no desenvolvimento de aplicações dentro do ambiente Microsoft. Possuimos diversos alvos que podemos atingir, como por exemplo: aplicações .NET, aplicações para telefones, aplicações para jogos (XBox), aplicações para tablets, etc. Apesar de existir certa simetria em alguns pontos, em outros já podem não ter tantas semelhanças assim, o que torna difícil o controle manual durante a escrita de uma biblioteca para atender qualquer uma estas plataformas.

Desde 2010 a Microsoft trabalha em um projeto para a criação de bibliotecas que possam ser compartilhadas entre diferentes plataformas, e com o Visual Studio 2012, já temos uma template de projeto exclusiva para isso: Portable Class Library. Ao criar um projeto deste tipo, você será obrigado a informar com quais tipos de projetos deseja que ela seja compatível. Isso garantirá com que você somente utilize membros que sejam comuns entre todas as plataformas selecionadas.

Geralmente algumas informações e funcionalidades estão expostas para serem consumidas remotamente, entre os mais diversos dispositivos. Sendo assim, é muito comum termos a necessidade de consumir os serviços expostos pela própria empresa, parceiros de negócios, etc., afim de reutilizar recursos que já estejam construídos e disponibilizados por terceiros. E como já era de se esperar, APIs REST estão cada vez mais populares, e ter um mecanismo para o consumo de forma simples destes tipos de serviços é essencial em qualquer plataforma.

Sabendo disso, a Microsoft criou uma classe chamada HttpClient, que é utilizada para abstrarir toda a complexidade na interação com APIs REST, e mais recentemente, a Microsoft criou a versão portável desta mesma classe. Com isso, poderemos reutilizar o consumo de APIs REST (HTTP) através de diferentes plataformas, sem a necessidade de reconstruir a cada novo projeto uma ponte de comunicação com estes tipos de serviços, e ainda, sem a necessidade de recorrer de classes de mais baixo nível para atingir o mesmo objetivo.

Em princípio a classe HttpClient fornece as mesmas funcionalidades expostas desde a sua criação, acrescentando alguns poucos métodos que expõem alguma funcionalidade específica para uma determinada plataforma. Para utilizar essa DLL em algum dos seus projetos (incluindo bibliotecas portáveis customizadas (Portable Class Libraries)), basta você adicionar o pacote “Http Client Libraries”, conforme mostrado na imagem abaixo:

Virtualização do Reflection

Reflection é uma técnica que utilizamos no .NET para lidar diretamente com os metadados, ou seja, extrair quais são os campos, propriedades, métodos, eventos, atributos, etc., para que possamos criar um código dinâmico, com o intuito de inspecionar ou até mesmo alterar valores ou a execução baseado no que temos em cada um dos tipos.

Uma das técnicas utilizadas pelo próprio .NET Framework, e também por nós, é a criação ou utilização de atributos que nos permitem decorar em diversos membros, para que eles guiem a execução, permitindo ao consumidor destes tipos, a ler e interpretá-los de acordo com uma determinada regra. Para exemplificar, abaixo temos alguns casos de uso destas técnicas:

  • O Visual Studio utiliza atributos decorados nas propriedades das classes (controles), para exibir cada uma delas na janela de propriedades da IDE.
  • O WCF obriga a interface que define o contrato do serviço estar decorada com o atributo ServiceContractAttribute.
  • Utilizamos o atributo AuthorizeAttribute para refinar o acesso às ações dos controllers do ASP.NET MVC.
  • Alguma funcionalidade específica em vossa aplicação.

Partindo deste princípio, podemos ter um atributo customizado que utilizamos para decorar em propriedades de um determinado tipo. Esse atributo basicamente define um rótulo customizado para cada propriedade onde ele é aplicado. Abaixo temos o atributo e a classe já fazendo uso do mesmo:

public class Exibicao : Attribute
{
    public string Label { get; set; }
}

public class LinhaDeRegistro
{
    [Exibicao(Label = “Nome”)]
    public string Nome { get; set; }

    [Exibicao(Label = “Código”)]
    public int Codigo { get; set; }

    [Exibicao(Label = “Valor”)]
    public decimal Valor { get; set; }

    public DateTime DataDaOcorrencia { get; set; }
}

É importante dizer que o atributo por si só não serve para nada. Precisamos de um código que o consuma e faça alguma coisa com ele. Para isso, temos um método que percorre as propriedades do objeto, e para cada uma delas, verifica se existe o atributo recém criado, e se existir, exibe a informação formatada utilizando o rótulo que foi definido durante o desenvolvimento.

public static void ExibirColunas(Type type, object value)
{
    foreach (var p in new type.GetProperties())
    {
        var a = p.GetCustomAttribute(typeof(Attribute)) as Exibicao;

        if (a != null)
            Console.WriteLine(
                “{0}: {1}”, a.Label, p.GetValue(value, null));
    }
}

Com este método criado, podemos fazer o uso dele passando o objeto contendo as informações e também o seu tipo. Apesar de conseguir extrair o Type no interior do próprio método, optei por deixar propositalmente via parâmetro, que mais tarde isso fará sentido. Abaixo o código que chama este método:

var linha = new LinhaDeRegistro()
{
    Codigo = 1,
    Nome = “Israel”,
    Valor = 12000,
    DataDaOcorrencia = DateTime.Now
};

ExibirColunas(linha.GetType(), linha);

Ao rodar a aplicação, o resultado será:

Nome: Israel
Código: 1
Valor: 12000

Como percebemos, o valor da propriedade DataDaOcorrencia não é exibida. Mas faz sentido, pois ela não foi decorada com o atributo Exibicao que criamos acima. Para resolver isso, basta, também, decorar o atributo na propriedade, compilar e rodar, que a informação será exibida conforme o esperado. Mas como faremos isso, se a classe LinhaDeRegistro está em um outro assembly, onde não temos acesso ao código fonte?

Para sanar esse problema, podemos recorrer à classe CustomReflectionContext, que está debaixo do namespace e assembly System.Reflection.Context.dll. Esse assembly faz parte do .NET Framework 4.5, que externaliza as capacidades de reflection de um determinado objeto, sem a necessidade de recriar um modelo total para isso.

A implementação padrão da classe abstrata CustomReflectionContext, simplesmente serve como um wrapper, sem qualquer mudança em relação ao tradicional. Mas isso nos dá a chance de customizar a criação de um novo contexto de reflection, e sobrescrevendo alguns métodos, nós podemos adicionar, remover ou alterar atributos ou até mesmo adicionar novas propriedades aos tipos que estamos manipulando.

Para exemplificar a sua utilização, criamos um contexto customizado, onde sobrescrevemos o método GetCustomAttributes, identificamos se o membro trata-se da propriedade DataDaOcorrencia, e se for, retornamos um atributo Exibicao configurado conforme é necessário por nossa aplicação (ou seja, pelo método ExibirColunas).

public class ContextoParaExibicao : CustomReflectionContext
{
    protected override IEnumerable<object> GetCustomAttributes(MemberInfo member,
        IEnumerable<object> declaredAttributes)
    {
        if (member.Name == “DataDaOcorrencia”)
            return new[] { new Exibicao() { Label = “Data da Ocorrência” } };

        return base.GetCustomAttributes(member, declaredAttributes);
    }
}

Finalmente, depois da criação deste contexto, precisamos utilizá-lo, e para isso, o instanciamos e chamamos o método MapType, que recebe como parâmetro um objeto do tipo TypeInfo, que também novo no .NET 4.5, e caracteriza a parte de “reflection” de um determinado tipo. Como essa classe herda da classe Type, podemos utilizar o retorno deste método, e passar diretamente para o método consumidor, sem a necessidade de alterar nada em seu interior:

ExibirColunas(
    new ContextoParaExibicao().MapType(linha.GetType().GetTypeInfo()),
    linha);

Apesar de algumas soluções já existirem no .NET Framework (TypeDescriptor e PropertyDescriptor) para tentar extrair essas mesmas informações, provendo também tipos específicos para a customização da extração (ICustomTypeDescriptor), o time do .NET Framework/CLR decidiu incorporar isso diretamente no .NET Framework, o que pode ser utilizado por todos aqueles que desejam essa funcionalidade, de dentro ou fora da Microsoft.

Compressão no .NET Framework 4.5

Há algum tempo, eu falava aqui sobre compressão de arquivos no .NET Framework. Temos à disposição as classes GZipStream e DeflateStream para manipular arquivos, ou melhor, conteúdos que necessitam ser compactados ou descompactados utilizando estes dois algoritmos (Deflate e GZip).

Apesar de funcionar em alguns cenários, é um pouco difícil trabalhar em situações mais comuns quando precisamos lidar com (des)compactação. Um exemplo típico é quando precisamos compactar um diretório todo, ou múltiplos arquivos em um único arquivo *.zip. Além disso, toda a manipulação precisa ser realizada através do uso de streams, ou seja, temos que lidar com a alocação e cópia dos bytes entre a origem e o destino.

Como as operações comuns são as mais complicadas de serem utilizadas, recorremos à frameworks externos, como é o caso do SharpZipLib, que fornece várias funcionalidades interessantes acerca da utilização de ZIPs. Finalmente, a partir da versão 4.5 do .NET Framework, a Microsoft incluiu novas classes que visa facilitar a manipulação destes tipos de arquivos em aplicações .NET.

Apesar dos novos tipos estarem debaixo do namespace System.IO.Compression, eles são fornecidos através da referência de novos assemblies que fazemos em nossas aplicações: System.IO.Compression.dll e System.IO.Compression.FileSystem.dll. O primeiro assembly fornece os tipos de mais baixo nível para trabalhar com ZIP; já as classes disponíveis dentro do segundo assembly, temos apenas alguns utilitários (classes estáticas) que tornam ainda mais fácil a geração e consumo de ZIPs pela aplicação.

A primeira classe que vamos falar é a ZipArchive, que corresponde a um pacote de arquivos comprimidos, que pode ser um ou N arquivos. Para cada conteúdo (arquivo) dentro do ZipArchive, existe uma classe que o descreve: ZipArchiveEntry. E como já era de se esperar, a classe ZipArchive fornece uma propriedade chamada Entries, que retorna a coleção de entradas, e podemos notar isso na imagem abaixo:

A classe ZipArchiveEntry não é criada diretamente, ou seja, através de um construtor público. A primeira opção para a sua criação, é através do método CreateEntry da classe ZipArchive, que recebe como parâmetro o nome da entrada, e que muitas vezes, se você estiver compactando um arquivo, o nome da entrada pode refletir o nome do arquivo que está sendo inserido.

No exemplo abaixo, abrimos a conexão com um arquivo de destino via FileStream. Esse arquivo será o arquivo compactado, ou seja, as entradas criadas serão colocadas nele. O stream que aponta para o arquivo Dados.zip, é passado no construtor da classe ZipArchive, e logo em seguida, percorremos os arquivos do diretório C:Temp, onde para cada arquivo, criamos uma entrada dentro do ZipArchive. Por fim, copiamos o conteúdo do arquivo da origem para o destino, ainda operando com streams. O código abaixo ilustra este trabalho:

using (var output = new FileStream(“Dados.zip”, FileMode.Create))
using (var archive = new ZipArchive(output, ZipArchiveMode.Create))
    foreach (var filename in Directory.GetFiles(@”C:Temp”))
        using (var source = File.Open(filename, FileMode.Open))
        using (var destination = archive.CreateEntry(Path.GetFileName(filename)).Open())
            source.CopyTo(destination);

Ao rodar esse código, efetuamos a compactação de todos os arquivos que estão no diretório C:Temp. Para os testes, existem dois arquivos neste diretório, totalizando cerca de 11 MB. E depois de serem compactados, e colocados no arquivo Dados.zip, o valor cai para 316 KB, assim como podemos perceber através da imagem abaixo:

Como comentei acima, com o intuito de facilitar ainda mais a criação de conteúdo compactado, vamos reescrever este código utilizando um novo método de estensão, chamado CreateEntryFromFile, que dado o arquivo de origem e o nome da entrada, ele automaticamente cria a mesma, já copiando todo o conteúdo para dentro dela. Vemos que o código fica mais simples ao utilizar essa opção:

using (var output = new FileStream(“Dados.zip”, FileMode.Create))
using (var archive = new ZipArchive(output, ZipArchiveMode.Create))
    foreach (var filename in Directory.GetFiles(@”C:Temp”))
        archive.CreateEntryFromFile(
            filename,
            Path.GetFileName(filename),
            CompressionLevel.Optimal);

E para facilitar ainda mais, temos um método estático disponível através da classe ZipFile, chamado CreateFromDirectory, que dado o endereço do diretório e o caminho para o arquivo de destino, ele já cria as entradas para cada arquivo, copia o conteúdo e, finalmente, gera o arquivo compactado. Por fim, em uma única linha, conseguimos reescrever os mesmos códigos que vimos acima:

ZipFile.CreateFromDirectory(@”C:Temp”, @”C:Dados.zip”);

Este método ainda fornece um overload, que além de permitir informar o nível da compactação, recebe um boleano, indicando se a pasta que está sendo compactada, deve ou não ser incluída como raiz do arquivo compactado. Abaixo, as imagens mostram quando utilizamos true, e logo na sequência, false, para este parâmetro:

ZipFile.CreateFromDirectory(
            @”C:Temp”,
            @”C:Dados.zip”,
            CompressionLevel.Optimal,
            true);

A extração do arquivo compactado para os arquivos isolados, são trabalhados de forma semelhante. E da mesma forma que na compactação, a descompactação também fornece alguns métodos utilitários que podem ser utilizados para realizar a extração dos arquivos que estão dentro do arquivo *.zip. Para exemplificar, no código abaixo, fazemos o uso do método estático ExtractToDirectory, exposto pela classe ZipFile. Este método recebe como fonte, o arquivo compactado, e o segundo parâmetro, corresponde ao caminho onde queremos extrair todo o conteúdo:

ZipFile.ExtractToDirectory(@”C:Dados.zip”, @”C:Output”);

Utilizando o DataReader Assincronamente

Na versão 2.0 do .NET Framework, a Microsoft incluiu uma série de novas funcionalidades em sua API de acesso a dados, o ADO.NET. Entre elas, podemos destacar o código genérico, MARS, Bulk-Copy e execução assíncrona de comandos e consultas.

O que tínhamos disponível naquela época é a implementação através do modelo assíncrono do .NET, que era implementado utilizando um par de métodos BeginXXX/EndXXX. Sendo assim, o método ExecuteReader passou a ter os métodos BeginExecuteReader e EndExecuteReader, enquanto o método ExecuteNonQuery, ganhou os métodos BeginExecuteNonQuery e EndExecuteNonQuery.

Da mesma forma, para ficar alinhado a nova forma de se trabalhar assincronamente nas linguagens, a execução assíncrona de comandos e consultas no ADO.NET 4.5 sofreu algumas mudanças, para seguir o modelo baseado em Tasks. Além das mudanças em nível das interfaces das classes, um detalhe importante é que não é mais necessário definir o flag Asynchronous Processing para True no arquivo de configuração, algo que sempre era descoberto somente depois que a aplicação estava em execução.

Para iniciar, a classe que representa a conexão (SqlConnection/DbConnection) fornecem a versão assíncrona do método Open, que é o OpenAsync. Este método retorna uma Task, o que a torna “aguardável”, e com isso, podemos utilizar a keyword await para que a abertura possa ser realizada de forma assíncrona. Abaixo o código ilustra o uso deste método:

private async static Task Executar()
{
    using (var conn = new SqlConnection(“…”))
    {
        await conn.OpenAsync();

        //…
    }
}

Como já era de se esperar, os mesmos métodos fornecidos na versão 2.0 do ADO.NET para processamento assíncrono, ganharam na versão baseada em Tasks na versão 4.5. No caso do ExecuteReader, temos o ExecuteReaderAsync. Já para o método ExecuteNonQuery, temos o ExecuteNonQueryAsync e, finalmente, para o ExecuteScalar, existe o ExecuteScalarAsync.

Todos estes métodos tratam-se da nova versão assíncrona, que basicamente retornam um objeto do tipo Task, que representa a tarefa que está sendo executada assincronamente. E, qualquer exceção que eventualmente ocorra dentro do processo assíncrono, ela será retornada/acessada através da Task que foi retornada pelo método. Abaixo temos um exemplo de como ler os dados através de DataReader, utilizando este novo modelo assíncrono:

private async static Task Executar()
{
    using (var conn = new SqlConnection(“…”))
    {
        await conn.OpenAsync();

        using (var cmd = new SqlCommand(“SELECT * FROM Cliente”, conn))
            using (var dr = await cmd.ExecuteReaderAsync())
                while (await dr.ReadAsync())
                    if (!await dr.IsDBNullAsync(1))
                        Console.WriteLine(dr.GetString(1));
    }
}

Acima utilizamos o método ExecuteReaderAsync, mas ao percorrer o result-set retornado, utilizamos o – também novo – método ReaderAsync, que é a versão assíncrona, também baseada em Task,  do método Read do DataReader. Esse método em conjunto os métodos NextResultAsync, IsDBNullAsync e GetFieldValueAsync<T>, fornecem um controle muito mais refinado aos dados que estão sendo extraídos, pois quando olhamos um pouco mais de perto os internals de cada um deles, percebemos que a versão síncrona pode custar caro, prejudicando assim a escalabilidade.

Além disso, todos os métodos que vimos até aqui, possuem um segundo overload que suporta o cancelamento da tarefa custosa que está sendo executada. Para controlar o cancelamento, eles fazem uso da estrutura CancellationToken, e que podemos criar e informar ao invocar o método. Com uma pequena mudança na assinatura do método de exemplo que criamos acima (Executar), ele passará a receber o token que controla e propaga a notificação de cancelamento. Uma vez que o mesmo é repassado às tarefas que são executadas internamente, periodicamente o token é consultado para ver se o cancelamento foi ou não solicitado. A mudança é ligeira:

private async static Task Executar(CancellationToken ct)
{
    using (var conn = new SqlConnection(“…”))
    {
        await conn.OpenAsync(ct);

        using (var cmd = new SqlCommand(“SELECT * FROM Cliente”, conn))
            using (var dr = await cmd.ExecuteReaderAsync(ct))
                while (await dr.ReadAsync(ct))
                    if (!await dr.IsDBNullAsync(1, ct))
                        Console.WriteLine(dr.GetString(1));
    }
}

Como percebemos, para preparar o método para permitir o cancelamento, é receber no parâmetro um CancellationToken, e propagá-lo para os métodos internos. Abaixo, estamos consumindo o método Executar que criamos, só que agora estamos passando um token que que será cancelado em dois segundos. Se depois deste tempo o método não concluir, uma exceção será disparada, informando que a tarefa foi cancelada.

var cts = new CancellationTokenSource();

try
{
    cts.CancelAfter(TimeSpan.FromSeconds(2));
    Executar(cts.Token).Wait();
}
catch (Exception ex)
{
    //trata exceção
}

Microsoft Fakes no Visual Studio 2011

Ao escrever testes unitários antes do código que define as regras dos negócios, conseguimos identificar uma série de detalhes que nos ajuda a definir uma interface clara e consistente para nossos tipos, afinal, já estamos tendo uma visão do consumidor dos mesmos.

Além disso, essa técnica nos ajuda a identificar eventuais dependências que nossos tipos exigem e, consequentemente, isolá-las para que tenham certa flexibilidade, que com isso conseguiremos alternar facilmente entre uma implementação dummy e outra real. E mesmo durante a execução dos testes, é comum utilizarmos uma implementação fictícia, para que ela nos auxilie a testar uma regra específica, onde é irrelevante o uso de uma dependência real.

Outra questão que geralmente ocorre ao escrever testes, é o uso (dependência) de recursos estáticos ou de alguma outra coisa que é díficil simular, como uma classe que não permite sobrescrever seus membros. Um outro grande exemplo dessa dificuldade, é quando temos um código a ser testado que é sensível à data/hora. Como a propriedade Now da estrutura DateTime sempre retorna o horário atual, chamá-la duas vezes, sempre retornará valores diferentes, o que fica difícil definir se o teste sucedeu ou não.

Como parte de diversas melhorias que foram adicionadas na parte de testes do Visual Studo 2011, a Microsoft também incluiu novos recursos para resolver estes pequenos problemas que vimos acima. Microsoft Fakes é framework que ajuda na criação de implementações dummies em nossos testes, onde podemos facilmente simular certas situações injetando um código customizado.

Primeiramente vamos analisar um cenário em que essas novas funcionalidades podem ser interessantes. A classe abaixo, Log, define a estrutura de um item a ser logado. Recebemos em seu construtor a mensagem, e na sequência ele atribui a data atual à propriedade Data.

public class Log
{
    public Log(string mensagem)
    {
        this.Mensagem = mensagem;
        this.Data = DateTime.Now;
    }

    public string Mensagem { get; private set; }

    public DateTime Data { get; set; }
}

Ao escrever um teste que avalie se a data é mesmo atribuída, ele não vai passar. Note que como recorremos à propriedade Now, ela sempre retorna uma data diferente, e por mais que sejam poucos milisegundos, é o suficiente para ser diferente e, consequentemente, o teste falhará.

[TestMethod]
public void DeveAtribuirDataAtualAoLogCriado()
{
    Assert.AreEqual(DateTime.Now, new Log(“Mensagem de Log”).Data);
}

Eis que entra em cena os Shims. Esse tipo de fake nos permite fornecer uma implementação alternativa a membros estáticos ou tipos que não podem ser facilmente customizados (via herança, por exemplo). Para habilitá-o, temos que ir até o projeto de testes, onde o assembly com os tipos que estão sendo testados está referenciado, e clicar com o botão direito em cima daquele que queremos criar os fakes, e em seguida, na opção “Add Fakes Assembly”, assim como vemos na imagem abaixo:

Isso pode ser feito em nossos próprios assemblies ou naqueles do .NET Framework, e independente de qual você escolha, um novo assembly será criado com a infraestrutura necessária para guiar e realizar essa simulação. Os shims são baseados em delegates, o que nos permitirá interceptar a chamada para qualquer método/propriedade, definindo uma “nova” implementação para eles, e que durante a execução, ela será executada, retornando assim, o resultado de acordo com a nossa necessidade. Abaixo a imagem ilustra os assemblies fakes que foram criados para os exemplos:

Logicamente que com esse procedimento, novos tipos foram criados. Como precisamos intervir no retorno da data atual, um novo tipo está disponível: ShimDateTime. A estrutura DateTime fornece a propriedade Now, e neste novo tipo, temos a propriedade NowGet (Get porque Now é somente leitura). Essa propriedade recebe a instância de um delegate do tipo Func<DateTime>, ou seja, deve apenas retornar um DateTime. Abaixo temos o código que exibe como configurá-la para retornar uma data específica.

[TestMethod]
public void DeveAtribuirDataAtualAoLogCriado()
{
    using (ShimsContext.Create())
    {
        ShimDateTime.NowGet = () => new DateTime(2012, 4, 18);

        Assert.AreEqual(DateTime.Now, new Log(“Mensagem de Log”).Data);
    }
}

Além dos tipos que foram criados durante o procedimento de criação dos fakes, existem alguns outros tipos que dão suporte à configuração e execução destes testes. O principal deles é ShimContext, que controla o tempo de vida dos shims. A forma mais fácil de criá-la é através do método estático Create, mas é extremamente importante envolve-lo em um bloco using, para delimitar claramente o bloco onde eles serão utilizados e, principalmente, encerrar o contexto quando os shims não forem mais necessários. Se não fizer isso, a propriedade NowGet retornará sempre 18 de abril de 2012.

Em uma outra situação, imaginemos uma classe que é responsável por avaliar certos índices, e quando eles ultrapassarem um certo valor, uma mensagem deve ser logada para posterior análise. O nosso monitor deverá depender de uma definição e não de uma implementação do repositório de logs, ou em outras palavras, a ideia é durante os testes, injetar um repositório que armazena os logs em memória, mas ao consumir isso por uma aplicação, utilizamos um repositório real, onde por exemplo, poderia gravar isso em um banco de dados. Abaixo temos a estrutura da interface IRepositorioDeLogs e da classe Monitor:

public interface IRepositorioDeLogs
{
    void Adicionar(Log log);

    IEnumerable<Log> Todos { get; }
}

public class Monitor
{
    private IRepositorioDeLogs repositorioDeLogs;

    public Monitor(IRepositorioDeLogs repositorioDeLogs)
    {
        this.repositorioDeLogs = repositorioDeLogs;
    }

    public void AvaliarIndice(double indice)
    {
        if (indice > 75)
            repositorioDeLogs.Adicionar(
                new Log(string.Format(“O índice está acima dos 75.”, indice)));
    }
}

Para efetuar o teste, precisamos passar uma classe que implemente a interface IRepositorioDeLogs, onde neste caso, adicionará os logs em uma lista na memória. Vale lembrar aqui, que os testes garantem que se o índice estiver acima, ele deve gravar o log, independente se está armazenado na memória ou não.

Ao invés de criar uma implementação dummy, podemos recorrer aos stubs, que também são fornecidos por este novo recurso do Visual Studio 2011. Stubs fornecem uma implementação padrão para cada um dos membros fornecidos, neste caso, pela interface IRepositorioDeLogs. Novamente, aqui também recorreremos ao uso de delegates para customizar a funcionalidade de cada membro.

Como criamos também os fakes para o assembly que contém nossos tipos, ele já criou a classe StubIRepositorioDeLogs. Essa classe fornece os campos AdicionarLog e TodosGet. O primeiro recebe um delegate do tipo Action<Log>, enquanto o segundo define um delegate do tipo Func<IEnumerable<Log>>. Com uma implementação simples, conseguiremos simular um repositório que conseguirá acomodar o log que será adicionado ao mesmo, mas isso se a regra da classe Monitor, alvo do teste, estiver em correta.

Para o exemplo, criamos uma coleção do tipo List<Log>, e no delegate do campo AdicionarLog, invocamos o método Add da coleção, enquando ao acessar a propriedade Todos, irá retorná-la na íntegra, e é justamente a propriedade Count que determinará o sucesso ou a falha do teste. O código abaixo ilustra esse procedimento utilizando o stub recém criado:

[TestMethod]
public void SeIndiceEstiverAcimaDe75DeveIncluirLog()
{
    var repositorio = new List<Log>();
    var stub = var StubIRepositorioDeLogs()
    {
        AdicionarLog = log => repositorio.Add(log),
        TodosGet = () => repositorio
    };

    var indice = 78D;
    new Monitor(stub).AvaliarIndice(indice);

    Assert.AreEqual(1, repositorio.Count);
}

Claims em todo lugar

Há algum tempo eu falei aqui sobre o WIF (Windows Identity Foundation), que consiste em um framework para tornar o gerenciamento de identidades em aplicações .NET mais simples. Ele traz uma série de funcionalidades, que quando utilizado nas aplicações, nos permite lidar com a autenticação e autorização de uma forma mais poderosa, conseguindo inclusive, terceirizar/centralizar isso para uma outra aplicação.

O modelo de objetos do WIF, definia duas novas interfaces: IClaimsIdentity e IClaimsPrincipal, que herdavam de duas interfaces já existentes no .NET Framework desde a sua primeira versão (IIdentity e IPrincipal, respectivamente). Apesar de indiretamente compatíveis com os principals do .NET (WindowsPrincipal e GenericPrincipal), havia um trabalho extra a se fazer quando a conversão fosse necessária, ou melhor, quando a aplicação queria trabalhar com claims ao invés do modelo tradicional.

Com o .NET Framework 4.5, a Microsoft decidiu incorporar o modelo de claims diretamente no .NET Framework ao invés disso ser fornecido exclusivamente pelo WIF. A partir da versão 4.5, o .NET Framework passa a fornecer nativamente os tipos necessários para trabalhar com claims, e ainda, fez com que as classes WindowsPrincipal e GenericPrincipal herdem de ClaimsPrincipal e, consequentemente, toda e qualquer aplicação ao ser migrada para a versão 4.5 do .NET, automaticamente poderá fazer uso de claims. O diagrama abaixo mostra a hierarquia da herança entre essas classes:

System.Security.Claims é um novo namespace que foi adicionado ao .NET Framework, com a finalidade de agrupar os tipos disponíveis para realizar o trabalho via claims. Abaixo temos um exemplo de como podemos fazer o uso de claims em aplicações que utilizam, por exemplo, um modelo de autenticação customizado:

using System;
using System.Security.Claims;
using System.Security.Principal;

namespace App
{
class Program
{
static void Main(string[] args)
{
var principal =
new GenericPrincipal(new GenericIdentity(“Israel Aece”), new [] { “Admin”, “IT” });

foreach (var item in principal.Claims)
Console.WriteLine(“{0}: {1}”, item.Type, item.Value);
}
}
}

Utilizando o evento FirstChanceException

Dependendo da tecnologia que estamos utilizando, há opções diferentes para o tratamento global de exceções, onde na maioria das vezes, nos preocupamos em efetuar o log de exceções não tratadas pela nossa aplicação. Por exceção não tratadas, entendam que sejam aquelas exceções que são disparadas e não estão envolvidas por um bloco try/catch.

Aquelas exceções que sabemos como lidar, prontamente interceptamos e exibimos uma mensagem amigável para o usuário, podendo ele ou o sistema, resolver ou sugerir a solução para o mesmo. Muitas vezes, para essas exceções “amigáveis”, não nos preocupamos em catalogá-las e, consequentemente, não sabemos com qual frequência elas estão sendo disparadas. Informação essa que poderíamos utilizar para melhorar e tentar contornar o problema, para assim evitar que ela seja disparada frequentemente.

Como podemos perceber, há várias situações em que nos deparamos com o tratamento de exceções. Centralizar o tratamento facilita o log, mas não permite resolver o problema no local onde ela ocorre; já o tratamento local nos permite refinar a mensagem e como resolver o problema, mas acaba com uma explosão de blocos de tratamento de erros e, eventualmente, inclui um código para logar estas exceções.

Para facilitar isso, a partir da versão 4.0 do .NET Framework, temos um novo evento que foi adicionado à classe AppDomain, chamado de FirstChanceException. Esse evento é disparado momentos antes da CLR procurar dentro da call stack um tratador (bloco try/catch) para este tipo de exceção. Mesmo que o código que dispara a exceção estiver envolvido por um bloco catch com uma exceção específica, primeiramente o evento em questão será disparado, para em seguida, entregar a exceção para o respectivo tratador. Isso permitirá interceptar toda e qualquer exceção que aconteça, mesmo aquelas que já estejam devidamente tratadas.

É importante dizer que este evento é meramente informativo, ou seja, isso quer dizer que se a exceção não estiver sendo tratada, ela irá danificar a sua aplicação. Não há nenhum flag que nos permita definí-la como tratada. O código abaixo ilustra a sua utilização:

AppDomain.CurrentDomain.FirstChanceException += (obj, e) => EfetuarLog(e.Exception);
try
{
    throw new Exception(“Algum Erro.”);
}
catch (Exception)
{
    Console.WriteLine(“Tratando o Erro.”);
}