O atributo InheritedExport do MEF

A Microsoft incorporou no .NET Framework 4.0 o MEF (Managed Extensibility Framework), qual podemos utilizar para criar extensões em nossas aplicações e/ou utilizá-lo como um container de injeção de dependências. E ainda quando estava em suas primeiras versões, eu escrevi um artigo sobre ele, abordando os princípios básicos de sua utilização.

Basicamente o MEF trabalha com atributos, que decorados nos tipos envolvidos, importam ou exportam partes que são utilizadas pelo container para construir e, consequentemente, atribuir às classes que utilizam tais instâncias. Para exemplificar, veja o exemplo abaixo, onde temos uma interface que define a estrutura para logs de instruções em algum arquivo no disco:

public interface ILogger
{
    string Path { get; set; }

    void Write(string message);
}

E depois disso, temos as classes que implementam essa interface, criando os tipos concretos de acordo com a nossa necessidade. Abaixo temos essas implementações, e em cada classe que implementa a interface IFileLogger, explicitamente temos que decorá-las com o atributo ExportAttribute, para informar ao container que aquela classe é uma implementação de uma determinada interface.

[Export(typeof(IFileLogger))]
public class CsvLogger : IFileLogger { }

[Export(typeof(IFileLogger))]
public class XmlLogger : IFileLogger { }

Só que se tivermos várias implementações, estamos obrigados a especificar classe a classe esse atributo para que o runtime possa utilizá-las. A finalidade deste artigo é apenas para apresentar um atributo que também faz parte do MEF, chamado de InheritedExportAttribute, que pode ser aplicado diretamente na definição (interface), que diz ao container que todos aqueles que o implementarem, são candidatos a serem utilizados pela aplicação, e com isso, não há mais a necessidade de especificar classe a classe o atributo ExportAttribute.

[InheritedExport]
public interface ILogger
{
    string Path { get; set; }

    void Write(string message);
}

Utilizar este atributo faz com que todas as implementações sejam adicionadas ao container. Se você quiser trabalhar da forma inversa, ou seja, optar por marcar somente aquelas que não devem ser adicionadas, então você pode utilizar o atributo PartNotDiscoverableAttribute, que previne que a implementação não seja incluída no catalogo, e consequentemente, não utilizada pelo container. Exemplo:

[PartNotDiscoverable]
public class FakeLogger : IFileLogger { }

Detalhes do uso de IQueryable

Como sabemos, a Microsoft incluiu nas linguagens .NET a capacidade de se escrever queries para interrogar coleções, de forma bem semelhante ao que fazemos quando desejamos trabalhar com alguma base de dados. Esse recurso ganhou o nome de LINQ, e várias funcionalidades e mudanças essas linguagens sofreram para conseguir acomodá-lo.

Para manipular um conjunto de informações, temos que ser capazes de executar duas tarefas: escrever/executar a query e iterar pelo resultado, caso ele exista. Parte disso já é suportado pelo .NET, através da interface IEnumerable<T>, que fornece um enumerador (representado pela interface IEnumerator<T>) para iterar por alguma coleção. A parte faltante (escrita) foi implementada também como uma interface e adicionada ao .NET Framework. Para isso, passamos a ter a interface IQueryable e IQueryable<T>. Com esses interfaces, somos capazes de expressar uma query através de uma AST (Abstract Syntax Tree), e mais tarde, traduzí-la para alguma fonte de dados específica.

Hoje temos os providers: LINQ To SQL, LINQ To XML e LINQ To Entities (Entity Framework). Cada um desses providers traduzem a query para uma fonte de dados específica, tais como: SQL Server (T-SQL), XML (XPath), ou até mesmo, Oracle (PL-SQL). A interface IQueryable<T> herda diretamente de IEnumerable<T>, para assim agregar a funcionalidade de iteração do resultado. A principal propriedade que ela expõe é a Expression, que retorna a instância da classe Expression, utilizada como raiz da expression tree. A imagem abaixo ilustra a hierarquia entre elas:

Existem duas classes dentro do assembly System.Core.dll, debaixo do namespace System.Linq, e que se chamam: Enumerable e Queryable. São duas classes estáticas que, via extension methods, agregam funcionalidades aos tipos IEnumerable<T> e IQueryable<T>, respectivamente. Grande parte dessas funcionalidades (métodos), refletem operadores para manipulação da query, tais como: Where, Sum, Max, Min, etc. A principal diferença entre essas duas classes é que enquanto a classe Enumerable recebe delegates como parâmetro, a classe Queryable recebe a instância da classe Expression (que envolve um delegate), que será utilizada para montar a query, e mais tarde, ser traduzida e executada pelo provider. Abaixo temos a assinatura do método Where para as duas classes:

namespace System.Data.Linq
{
    public static class Enumerable
    {
        public static IEnumerable<TSource> Where<TSource>(
            this IEnumerable<TSource> source, Func<TSource, bool> predicate)
        {
            //…
        }
    }

    public static class Queryable
    {
        public static IQueryable<TSource> Where<TSource>(
            this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
        {
            //…
        }
    }
}

Ambas interfaces postergam a execução da query até o momento que você precisa realmente iterar pelo resultado. Da mesma forma, como IQueryable<T> herda de IEnumerable<T>, você pode definí-la como retorno de uma função, e mais tarde, iterar pelo resultado. Só que utilizar uma ou outra, tem um comportamento bastante diferente, que pode prejudicar a performance. Para exemplificar, vamos considerar um método que retorna as categorias da base de dados, e depois disso, vamos aplicar um filtro para retornar somente aquelas categorias cujo Id seja maior que 2. Com isso, escrevemos o seguinte código:

class Program
{
    private static DBTestesEntities ctx = new DBTestesEntities();

    static void Main(string[] args)
    {
        var query = RecuperarCategorias().Where(c => c.Id > 2);

        foreach (var item in query)
            Console.WriteLine(item.Descricao);
    }

    static IEnumerable<Dados> RecuperarCategorias()
    {
        return from c in ctx.Categorias select c;
    }
}

Repare que depois de executar o método RecurarCategorias, invocamos o método de estensão Where, e neste caso, fornecido pela classe Enumerable, já que o retorno da função é do tipo IEnumerable<T>. Ao compilar a aplicação, o compilador faz o parse da query LINQ e a transforma em dados, fazendo com que a query seja representada por objetos do tipo Expression. Abaixo temos o código decompilado, e podemos perceber que a expressão lambda que passamos para o método Where, está sendo utilizada para filtrar o resultado.

internal class Program
{
    private static DBTestesEntities ctx = new DBTestesEntities();

    private static void Main(string[] args)
    {
        IEnumerable<Categoria> query = RecuperarCategorias().Where<Categoria>(delegate (Categoria c) {
            return c.Id > 2;
        });

        foreach (Categoria item in query)
            Console.WriteLine(item.Descricao);
    }

    private static IEnumerable<Categoria> RecuperarCategorias()
    {
        ParameterExpression CS$0$0001;
        return ctx.Categorias.Select<Categoria, Categoria>(
            Expression.Lambda<Func<Categoria, Categoria>>(CS$0$0001 = Expression.Parameter(typeof(Categoria), “c”), 
            new ParameterExpression[] { CS$0$0001 }));
    }
}

O grande detalhe aqui é que as estensões para a interface IEnumerable<T>, recebem delegates como parâmetro, o que quer dizer que o filtro será aplicado do lado cliente, ou seja, ao invocar o método RecuperarCategorias, todas elas (categorias) serão retornadas, carregadas em memória, e depois aplicado o filtro, “descartando” aqueles elementos que não se enquadram no critério. Abaixo temos a query que chegou até o SQL Server:

SELECT
    [Extent1].[Id] AS [Id], 
    [Extent1].[Descricao] AS [Descricao]
FROM [dbo].[Categorias] AS [Extent1]

Agora, se o retorno da função RecuperarCategorias for do tipo IQueryable<Categoria>, o comportamento é totalmente diferente. O simples fato de alterar o tipo de retorno, é o suficiente para a compilação ser substancialmente diferente. No código abaixo temos esse código para ilustrar. Note que depois de invocar o método RecuperarCategorias, já temos o método Where na sequência. A diferença é que o método Where aqui é oriundo da classe Queryable, que ao invés de receber um delegate, recebe uma Expression, obrigando o compilador a reescrever o nosso código, transformando o filtro “c => c.Id > 2” em uma instância da classe Expression, expressando aquele mesmo critério.

internal class Program
{
    private static DBTestesEntities ctx = new DBTestesEntities();

    private static void Main(string[] args)
    {
        ParameterExpression CS$0$0000;
        IQueryable<Categoria> query = RecuperarCategorias().Where<Categoria>(
            Expression.Lambda<Func<Categoria, bool>>(
                Expression.GreaterThan(Expression.Property(CS$0$0000 = Expression.Parameter(typeof(Categoria), “c”), 
                (MethodInfo) methodof(Categoria.get_Id)), Expression.Constant(2, typeof(int))), 
            new ParameterExpression[] { CS$0$0000 }));

        foreach (Categoria item in query)
            Console.WriteLine(item.Descricao);
    }

    private static IQueryable<Categoria> RecuperarCategorias()
    {
        ParameterExpression CS$0$0001;
        return ctx.Categorias.Select<Categoria, Categoria>(
            Expression.Lambda<Func<Categoria, Categoria>>(CS$0$0001 = Expression.Parameter(typeof(Categoria), “c”), 
            new ParameterExpression[] { CS$0$0001 }));
    }
}

Apesar da ligeira mudança em tempo de compilação, isso permite o processamento remoto da query. Utilizar IQueryable<T> neste cenário, irá possibilitar o build-up de queries, ou seja, a medida que você vai adicionando critérios, operadores, etc., ele irá compondo a query principal e, finalmente, quando precisar iterar pelo resultado, apenas uma única query será disparada no respectivo banco de dados, já fazendo todo o filtro por lá, retornando menos dados. Isso pode ser comprovado visualizando a query que foi encaminhada para o banco de dados depois da alteração acima:

SELECT
    [Extent1].[Id] AS [Id], 
    [Extent1].[Descricao] AS [Descricao]
FROM [dbo].[Categorias] AS [Extent1]
WHERE [Extent1].[Id] > 2

Reportando o Progresso de Tarefas Assíncronas

Uma das necessidades que circundam o processamento assíncrono de alguma tarefa é como reportar o progresso da mesma, para dar um feedback, na maioria das vezes para um usuário, do estágio em que ela se encontra. Na maioria das vezes, isso está associado com interfaces gráficas (GUI), que por sua vez, podem trabalhar utilizando o modelo síncrono ou assíncrono.

Optamos pelo modelo assíncrono justamente permitir que o usuário possa realizar outras tarefas, enquanto aquela que é custosa seja processada por uma segunda thread. É neste ponto que temos a necessidade de saber como anda o progresso do processamento, repassando ao usuário onde se encontra a execução da tarefa.

A Microsoft trabalha em um projeto para tornar a programação assíncrona tão simples quanto a programação síncrona. Este projeto introduz duas keywords, chamadas async e await, que fazem todo o trabalho para “recriar” o código que escrevemos, montando-o da forma necessária para executá-lo de forma assíncrona, sem a necessidade de precisarmos saber como isso é realizado.

Para reportar o progresso temos dois tipos que foram introduzidos: a interface IProgress<T> e a classe EventProgress<T>. A interface genérica IProgress<T> define a estrutura de reporte de progresso, e que não fornece nada mais do que um método chamado Report, que recebe um parâmetro do tipo T, que pode ser qualquer tipo, desde um simples inteiro até uma classe que reporte informações mais detalhes do progresso da tarefa. Já a classe EventProgress<T> é uma implementação desta interface que acabamos de ver, fornecendo a opção de sermos notificados através de um evento. A imagem abaixo mostra a estrutura desses tipos:

A instância da classe EventProgress<T> pode ser criada de duas formas: através da instância diretamente ou através do método estático From. Mas a segunda opção, na versão atual (ainda em CTP), está com um problema, disparando uma exceção de forma indevida. Para exemplificar, temos um formulário Windows Forms, que ao clicar em um botão do mesmo, executaremos uma tarefa custosa, e que deverá reportar ao usuário o progresso da mesmo, utilizando esses tipos que vimos agora, em conjunto com as keywords async/await.

O primeiro passo é instanciar a classe EventProgress<T>, onde o tipo genérico T, será definido como um número inteiro, indicando a porcentagem do progresso, que utilizaremos para incrementar um controle ProgressBar. Depois da classe instanciada, vamos nos vincular ao evento ProgressChanged, que será disparado todo o momento em que o processo custoso desejar reportar o andamento do progresso do mesmo, e como já era de se esperar, um número inteiro será passado como parâmetro através da instância da classe EventArgs<T>, que possui um parâmetro genérico do mesmo tipo em que foi informado na construção da classe EventProgress<T>, acessível através da propriedade Value. O código abaixo ilustra essa parte inicial:

private EventProgress<int> report = new EventProgress<int>();

public Form1()
{
    report.ProgressChanged += (sender, args) => this.progressBar1.Value = args.Value;
}

Com a classe que servirá como forma de reportar o progresso, vamos agora criar o código que simula uma operação custosa, e invocá-lo a partir do evento Click de um botão qualquer. Note que o método ExecutarTarefaCustosa já está decorado com a keyword async. Além disso, temos a keyword await definida no interior do mesmo método, que determina que aquilo que vem na frente dela, é considerado como o callback.

private void button1_Click(object sender, EventArgs e)
{
    ExecutarTarefaCustosa();
}

private async void ExecutarTarefaCustosa()
{
    for (int i = 1; i <= 10; i++)
    {
        await TaskEx.Delay(TimeSpan.FromSeconds(2)); //Simula Processamento Custoso

        ((IProgress<int>)report).Report(i * 10);
    }
}

A cada etapa concluída (iteração), reportamos o progresso através da instância da classe EventProgress<T>. O único detalhe interessante, é que a interface IProgress<T> foi implementada de forma explícita, o que nos obriga a fazermos o casting para conseguir acessar o método Report, informando o parâmetro que representa o status do progresso.

Só que quando trabalhamos com tecnologias como Windows Forms ou WPF, há uma certa dificuldade em fazer a “conversação” entre as threads envolvidas. Por padrão, essas tecnologias utilizam apenas uma única thread, que é criada durante o início do processo. Com isso, todos os controles são criados e gerenciados por essa thread específica. Quando delegamos o processamento assíncrono para uma segunda thread, ela não poderá acessar os controles, pois os mesmos tem afinidade com a thread de criação.

Sabendo desta limitação, a classe EventProgress<T> faz uso de um SynchronizationContext internamente. Essa classe tem como finalidade fornecer uma forma de enfileirar trabalhos a serem realizados em um contexto (thread) específico. Ao criar a instância da classe SynchronizationContext, ela estabelece um vínculo com a thread onde ela está sendo criada, e ao acessá-la dentro de uma segunda thread, ele encaminhará o trabalho para ser realizado na thread ao qual está vinculada.

Finalmente, ao instanciar a classe EventProgress<T>, ela captura o SynchronizationContext corrente. Com isso, quando invocamos o método Report, ele irá disparar o método que vinculamos ao evento ProgressChanged, através do método Post da classe SynchronizationContext, que despacha (também de forma assíncrona) a execução para a thread que criou os controles, que seguramente poderá acessá-los.

Caching do .NET Framework

Desde a primeira versão do ASP.NET, a Microsoft incorporou nele recursos de caching (System.Web.Caching), com a finalidade de colocar em memória, informações que são custosas o bastante para carregar e/ou montar a todo momento, ou seja, apenas a primeira requisição pagaria o preço da carga/montagem, e as requisições subsequentes, passam a utilizar a informação que já está em memória, melhorando consideravelmente a performance.

O maior problema daquela API, é a forte afinidade com o ASP.NET, mais especificamente, com o assembly System.Web.dll, o que fica difícil de reutilizar a mesma em outras aplicações que não sejam web. Caching é um recurso útil para qualquer tipo de aplicação ou serviço, ou seja, qualquer um pode fazer uso deste recurso afim de melhorar a performance.

Sabendo desta dificuldade, o time de Patterns & Practices da Microsoft, decidiu criar um framework para atender esta demanda. Este framework foi chamado de Caching Application Block, e que foi incorporado ao Enterprise Library. Como isso é uma necessidade de grande parte das aplicações, e para alinhar com outros produtos da Microsoft, ela decidiu incorporar este recurso no .NET Framework, e a partir da versão 4.0 do mesmo, já há um assembly chamado System.Runtime.Caching.dll, que fornece os tipos necessários para permitir que nossas aplicações façam uso de estrutura de caching sem a necessidade de recorrer à componentes extras. A ideia deste artigo é explorar esse assembly e os principais tipos que ele fornece.

O primeiro tipo que vamos analisar neste assembly é o ObjectCache. Esta classe representa o caching em si, armazenando aqueles objetos que desejamos manter no cache. Esta classe é abstrata, e serve como base para criar qualquer nova implementação de cache em memória, fornece métodos comuns para qualquer estrutura de caching, tais como: Add, Get e Remove. Através da imagem abaixo, podemos perceber que esta classe implementa a interface IEnumerable<KeyValuePair<string, object>>, para armazenar e iterar pelos elementos que estarão contidos no cache.

Na imagem acima podemos visualizar um tipo chamado MemoryCache, que herda diretamente da classe ObjectCache. Essa classe é uma implementação para caching em memória, bem próxima aquela que utilizamos com o ASP.NET (System.Web.Caching.Cache), mas como sabemos, com o benefício de ser utilizado por qualquer tipo de aplicação.

O primeiro passo para trabalhar com o caching, é justamente criar a instância do “container”, que servirá como repositório, em memória, para todos os elementos que desejamos colocar em caching. Para o exemplo, vamos utilizar a implementação que existe no .NET Framework, que é a MemoryCache. Essa classe já fornece uma propriedade estática, chamada Default. Essa propriedade tem a finalidade de retornar sempre uma instância padrão da mesma, que para grande partes dos cenários, somente uma instância dela é usada por toda a aplicação.

A criação da instância da classe MemoryCache é criada quando se acessa a propriedade Default pela primeira vez, e a partir daí, compartilha a mesma instância com toda a aplicação, desde que ela seja sempre acessada a partir desta propriedade. Você pode criar uma nova instância da classe MemoryCache ao invés de utilizar a propriedade Default, mas você terá que saber como lidar e acessar essa nova instância na aplicação. Se optar por instanciar diretamente, você deve informar uma string contendo um nome que você dará à ela, que será utilizado pelo sistema de configuração, que será comentado mais tarde, ainda neste artigo.

Depois disso, o uso desta API é bastante trivial, bem próximo ao que já conhecemos quando manipulamos qualquer estrutura de caching. O método Add possui algumas versões (sobrecargas), onde cada uma delas permite informar um conjunto diferente de parâmetros, mas com um único propósito, o de acomodar corretamente o item dentro do caching. Uma das versões do método recebe um tipo chamado CacheItem. O CacheItem representa um elemento individual dentro do cache, que recebe duas principais informações: chave e valor, que são autoexplicativas. É importante informar que o valor sempre é do tipo System.Object, o que nos obrigará a trabalhar com conversões durante a extração do elemento do cache. O código abaixo é um exemplo de como adicionar um dado dentro do caching:

MemoryCache cache = MemoryCache.Default;

if (!cache.Contains(“chave”))
    cache.Add(new CacheItem(“chave”, DateTime.Now), null);

O método Add retorna um valor boleano indicando se o item foi ou não adicionado com sucesso. Caso a chave já exista dentro do cache, esse método retornará False. Já para a extração do item de cache, basta recorrer ao indexer passando o nome da chave, ou ainda, utilizando LINQ, já que o caching implementa a interface IEnumerable<T>.

//Via Indexer
Console.WriteLine(cache[“chave”]);

//Via LINQ
var itens =
    (from x in cache where x.Key == “chave” select x.Value).OfType<DateTime>();

Políticas de Caching

Uma das preocupações ao adicionar um item no cache, é o período de tempo em que ele deverá permanecer por lá. Isso é importante para garantir que a informação não permaneça lá por um tempo em que a torne inválida, devolvendo para os seus clientes dados que já estão defasados.

Com a finalidade de controlar a periodicidade do item no cache, temos uma classe chamada CacheItemPolicy. Cada instância desta classe estará associada com um item no cache, e deve ser informada no momento da adição do item no cache. Entre as principais propriedades desta classe, temos AbsoluteExpiration e SlidingExpiration. A primeira determina um DateTime que representa uma data precisa para expiração do item; já a SlidingExpiration determina um TimeSpan que irá renovar o item no cache se ele for acessado dentro daquele período de tempo, caso contrário, será expirado.

Há também uma propriedade chamada Priority, que como o próprio nome diz, determina a prioridade do item no caching, caso a memória seja necessária por outro recurso, o item será descartado levando em consideração a prioridade dele. Há ainda duas propriedades que determinam callbacks, e que são utilizados para interceptar alguns pontos do runtime. A propriedade RemovedCallback recebe o ponteiro para um método que segue a assinatura imposta pelo delegate CacheEntryRemovedCallback, e será invocado depois que o item for removido do cache. Isso permite você criar alguma regra para recolocar o item no cache, para evitar que você faça isso somente quando o item for novamente exigido. Abaixo temos um exemplo da utilização deste callback:

cache.Add(new CacheItem(“chave”, DateTime.Now), new CacheItemPolicy()
{
    RemovedCallback = args => RecarregarInformacao(args),
    AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(2)
});

Há uma restrição com relação a quantidade de memória total que pode ser utilizada pelo MemoryCache. O limite pode/deve ser especificado através da propriedade CacheMemoryLimit, que recebe um valor (representado por um Int64), que corresponde em megabytes, a quantidade máxima que poderá ser alocada para os itens do cache. O valor padrão (zero) permite ao próprio cache, controlar a quantidade de memória disponível pelo computador onde a aplicação está rodando.

Monitores

Nem sempre a expiração através de um período de tempo (renovável ou não) é o suficiente para determinar a remoção do item do cache. As vezes podemos nos dar o luxo de esperar até que um outro elemento seja alterado para notificar que o item do cache deve ser removido. Quando dimensionamos o tempo de expiração ele pode ser curto ou longo demais, e isso pode acarretar em problemas na visualização dos dados que lá estão.

Um exemplo clássico é manter o cache de um result-set proveniente de uma tabela do banco de dados, e manter o cache até que alguma ação (inserção, atualização ou exclusão) que justifique a mudança seja executada naquela tabela. Aqui estamos dependendo de um tabela do banco de dados. Podemos nos basear em um arquivo, que qualquer mudança nele, resulte na remoção do item do cache.

Para atender essa necessidade, a Microsoft incluiu neste assembly os monitores. Alguns monitores já foram criados por ela, e disponibilizados para usá-los em nossas aplicações. A classe abstrata ChangeMonitor é a base para qualquer monitor, fornecendo toda a estrutura necessária para que as classes derivadas possam detectar a mudança em algum recurso e, consequentemente, notificar que isso ocorreu, permitindo ao monitor notificar a nossa aplicação (cache). A imagem abaixo ilustra a hierarquia dos monitores do .NET Framework:

O SqlChangeMonitor recebe em seu construtor a instância da classe SqlDependency, que reportará qualquer alteração em uma tabela específica e, consequentemente, passará a informação para o monitor de cache, que por sua vez, removerá o item associado ao mesmo. Já o monitor CacheEntryChangeMonitor serve para monitorar as entradas no cache, e é usado quando o cache precisa monitorar seus próprios itens. FileChangeMonitor é a classe base para monitorar o sistema de arquivos. A implementação nativa para esta classe, é a classe HostFileChangeMonitor, que irá monitorar algumas ações que eventualmente aconteçam em um arquivo ou diretório, e irá repassar para o cache. Internamente esta classe recorre ao tradicional FileSystemWatcher. O exemplo abaixo ilustra a sua utilização:

CacheItemPolicy policy = new CacheItemPolicy();
policy.ChangeMonitors.Add(new HostFileChangeMonitor(new[] { @”C:TempTeste.txt” }));
cache.Add(new CacheItem(“chave”, DateTime.Now), policy);

O construtor da classe HostFileChangeMonitor recebe uma coleção de paths para arquivos e/ou diretórios que deseja monitorar. Depois de instanciado e devidamente configurado, ele deverá ser adicionado na coleção de monitores, exposta pela classe CacheItemPolicy, através da propriedade ChangeMonitors, onde um item no cache poderá depender de qualquer alteração que ocorra em algum dos monitores associados à ela.

Configuração

Algumas poucas configurações do cache, estão disponíveis para serem alteradas através do arquivo de configuração da aplicação. Isso permite que adequarmos o runtime mesmo depois da aplicação instalada, sem a necessidade de recompilar a mesma. Há uma seção chamada <system.runtime.caching />, que podemos configurar os caches que nossa aplicação utiliza.

O vínculo entre o cache criado pela aplicação e o arquivo de configuração, se dá pelo nome do mesmo. Quando utilizamos a propriedade estática Default da classe MemoryCache, a instância da mesma é criada, e o nome dela é definido como “Default”. Quando decidimos criarmos mais do que uma instância da classe MemoryCache, somos obrigados a utilizar o construtor público, que exige uma string representando o nome do cache. Esse nome poderá ser utilizado para se relacionar com o arquivo de configuração.

O exemplo abaixo permite a configuração do cache padrão. Estamos configurando informações pertinentes à memória, como já foi falado acima. Ainda temos uma outra configuração (que também é possível através do modelo imperativo), que define o intervalo (PollingInterval) em que o cache compara o que temos armazenado com o limite especificado, para evitar o constante acesso ao mesmo.

<?xml version=”1.0″?>
<configuration>
  <system.runtime.caching>
    <memoryCache>
      <namedCaches>
        <add name=”Default”
             cacheMemoryLimitMegabytes=”1024″
             pollingInterval=”00:05:00″ />
      </namedCaches>
    </memoryCache>
  </system.runtime.caching>
</configuration>

Contadores de Performance

Finalmente, para monitorar a saúde do caching que está sendo utilizado pelo aplicação, podemos recorrer à alguns contadores de performance que são instalados pelo próprio .NET Framework, que tem a finalidade de coletar e exibir informações que retratam a situação atual do caching. Para visualizar, podemos recorrer à ferramenta Performance Monitor do Windows, e procurar pela categoria .NET Memory Cache 4.0. Lá você terá uma instância para cada aplicação que utiliza este recurso.

Conclusão: No decorrer deste artigo, pudemos analisar este novo assembly que traz uma porção de tipos para agregarmos a nossa aplicação/componente recursos de caching, algo que antes era somente possível se utilizando de um componente a parte, quando não aquele fornecido para trabalhar exclusivamente com aplicações ASP.NET.

System.Json no .NET Framework

Para aqueles que estiveram presentes na minha palestra sobre REST no TechEd 2010, puderam ver as opções que temos para a construção e consumo de serviços baseados neste modelo. Tive a oportunidade de mostrar de forma superficial, cada uma das tecnologias que circundam o WCF, e que foram construídas em cima dele, para atender a necessidade atual.

Já falei aqui sobre o WCF REST Starter Kit (HttpClient), que torna o consumo de serviços REST (construídos ou não em .NET) muito mais simples em aplicações .NET (Console, Windows, WPF, ASP.NET, etc.), abstraindo a necessidade de conhecer detalhes internos do protocolo HTTP, lidando facilmente com os formatos existentes, tais como: Xml, Atom e JSON.

O WCF já fornece suporte a construção de serviços REST há algum tempo. Temos a possibilidade de expor serviços neste modelo, possibilitando a serialização em qualquer um dos formatos acima, mas ainda, com um pequeno suporte para lidar com objetos que são criados dinamicamente por seus clientes. Isso quer dizer que o WCF, até então, exige que o cliente deva conhecer a estrutura do objeto que o serviço espera. O problema é que isso pode ser dinamicamente criado pelos clientes, algo que o JavaScript pode fazer muito bem.

Para ter uma integração maior com estes tipos de clientes, a Microsoft está incorporando ao WCF recursos para suportar melhor este tipo de cenário. Para isso, um novo conjunto de classes está sendo colocando dentro do .NET Framework, que representarão objetos JSON, algo que o Silverlight já possui há algum tempo, através de uma API chamada System.Json. Agregando isso no .NET Framework e, consequentemente, no WCF, podemos construir serviços que recebam e retornem objetos JSON, podendo combinar isso com variáveis do tipo dynamic, que também são suportadas pela linguagem.

É importante dizer que o que temos disponível atualmente, ainda está em fase de desenvolvimento, e o que verá aqui, poderá sofrer alguma alteração até a versão final. Isso está disponível no Codeplex, neste endereço. Várias novidades estão disponíveis, mas o foco neste artigo é apresentar a integração melhorada com JSON. Ao instalar este pacote, temos dois assemblies que compõem estas novas funcionalidades: Microsoft.Runtime.Serialization.Json.dll e Microsoft.ServiceModel.Web.jQuery.dll.

O primeiro assembly é onde temos o namespace System.Json, assim como no Silverlight. É dentro dele que estão todas as classes que servem para construir e representar objetos em formato JSON. A base para todas elas é a JsonValue, que define a estrutura de um objeto JSON dentro do .NET Framework, contendo também as funcionalidades comuns para todos os outros tipos JSON: JsonPrimitive, JsonObject e JsonArray. Todos herdam diretamente de JsonValue, implementando as funcionalidades específicas para cada uma delas.

Como sabemos, a representação JSON de um objeto nada mais é que um dicionário, e devido a isso, a classe JsonValue implementa a interface IEnumerable<KeyValuePair<string, JsonValue>>, para permitir a iteração dos valores que ela armazena internamente. JsonPrimitive, como o próprio nome diz, refere-se aos tipos primitivos suportados no JavaScript: String, Number e Boolean. Depois temos a classe JsonObject, que define uma coleção de JsonValue, e seu indexador é do tipo string (chave) e, finalmente, o JsonArray trata-se de uma coleção que armazena instâncias da classe JsonValue, e seu indexador é um número inteiro. Abaixo temos uma imagem que mostra a hierarquia destas classes:

No outro assembly, temos a implementação de objetos específicos que fazem parte da infraestrutura do WCF, criados para que o mesmo consiga facilmente receber e interprertar as requisições realizadas que recebem e retornam objetos específicos para este modelo. Entre as classes que fazem parte deste novo assembly, temos: WebHttpBehavior3, WebServiceHost3 e WebserviceHostFactory3, seguindo a mesma linha das classes que foram criadas quando o REST foi introduzido no WCF. Basicamente a necessidade destas classes é justamente permitir que o behavior WebHttpBehavior3 seja acoplado a execução, que efetua algumas validações na requisição para determinar se ela está de conformidade com os objetos suportados, e além disso, acopla também um tratador de erro específico, convertendo eventuais problemas em objetos do tipo JsonObject.

Para exemplificar, vamos criar um serviço que será responsável por armazenar uma lista de itens, que será manipulada por um formulário HTML, recorrendo ao jQuery para dialogar com o respectivo serviço. O detalhe mais importante a se notar, são os tipos envolvidos nos parâmetros e no retorno dos métodos, que como podemos perceber, fazem parte da nova API que vimos acima. Abaixo temos a implementação deste serviço:

[ServiceContract]
public class ServicoDeCache
{
    private List<JsonObject> itens = new List<JsonObject>();

    [WebInvoke]
    public void Adicionar(JsonObject item)
    {
        this.itens.Add(item);
    }

    [WebGet]
    public JsonObject Extrair(int codigo)
    {
        return 
            (
                from i in this.itens 
                where i.AsDynamic().Codigo.ReadAs<int>() == codigo 
                select i
            ).SingleOrDefault();
    }

    [WebGet]
    public JsonArray Recuperar()
    {
        return new JsonArray(this.itens.ToArray());
    }
}

Os métodos são autoexplicativos, e manipulam uma lista interna que serve como repositórios para os itens. O método Extrair é aquele que merece uma maior atenção. Ele recebe um inteiro como parâmetro e retorna a instância da classe JsonObject, recorrendo à “LINQ To JSON” para extrair o elemento solicitado pelo cliente. O que chama atenção também é o método AsDynamic, que é declarado na classe JsonValue, e como havia mencionado, converte a instância do objeto atual em dynamic, permitindo o acesso as propriedades criadas pelo cliente de forma dinâmica (late-bound). Depois temos o método genérico ReadAs<T>, que também está acessível através da classe JsonValue, que recorre ao serializador JSON para tentar extrair o valor tipado daquela propriedade específica.

Observação: Como podemos perceber, diferentemente de serviços que construímos até então, a classe que representa o serviço não implementa uma interface de contrato. Isso se deve ao fato de serviços REST não possuem um contrato definido, sem geração de metadados e sem operações (já que serviços REST são baseados em recursos). Agradeço aqui ao Carlos Figueira, membro do time de WCF, que me esclareceu essa questão.

Depois do serviço implementado, temos que nos preocupar em expor o serviço através do host correto, e como vimos acima, deverá ser através do WebServiceHost3. Existem algumas formas de fazer isso, e uma delas é utilizar o Url Routing para determinar que o host para este serviço deverá ser o WebServiceHost3. Isso já é o suficiente para deixar o serviço disponível para consumo, pois ele se encarrega de criar os endpoints necessários, sem ter necessidade de criar qualquer configuração no arquivo Web.config. O código abaixo deve ser declarado no arquivo Global.asax, para registrar a rota.

public class Global : HttpApplication
{
    private void Application_Start(object sender, EventArgs e)
    {
        this.RegisterRoutes();
    }

    private void RegisterRoutes()
    {
        RouteTable.Routes.Add(
            new ServiceRoute(
                “ServicoDeCache”, 
                new WebServiceHostFactory3(), 
                typeof(ServicoDeCache)));
    }
}

Finalmente, depois de toda essa configuração reliazada, chega o momento de consumir o serviço através de algum cliente. Poderia ser através do Fiddler, mas para exemplificar melhor, vamos utilizar o jQuery para invocar duas das operações que disponibilizamos acima: Adicionar e Recuperar.

http://scripts/jquery-1.4.2.min.js
http://scripts/json2.js

    function Adicionar() {
        var nome = $(‘#Nome’).val();
        var codigo = $(‘#Codigo’).val();
        var item = { Nome: nome, Codigo: JSON.parse(codigo) };

        $.ajax(
            {
                type: ‘POST’,
                url: ‘./ServicoDeCache/Adicionar’,
                dataType: ‘json’,
                contentType: ‘application/json’,
                data: JSON.stringify(item),
                processData: false,
                success:
                    function () {
                        alert(‘Item cadastrado com sucesso.’);
                    }
            });
    }

    function Recuperar() {
        $.ajax(
            {
                type: ‘GET’,
                url: ‘./ServicoDeCache/Recuperar’,
                dataType: ‘json’,
                contentType: ‘application/json’,
                data: null,
                processData: false,
                success:
                    function (itens) {
                        $(‘#Itens’).empty();

                        $.each(itens, function (indice, item) {
                            $(‘#Itens’)
                                .append($(”)
                                .attr(‘value’, item.Codigo)
                                .text(item.Nome));
                        });
                    }
            });
    }

O primeiro método constrói um objeto com duas propriedades: Codigo e Nome, que são abastecidas com informações do formulário, e em seguida, utilizamos a biblioteca JSON2 para transformá-lo em JSON. Já o método Recuperar retorna um array contendo os elementos que foram adicionados na coleção interna do serviço, recorrendo ao método $.each do jQuery para iterar pelos dados e exibí-los na tela. Se quiser saber mais sobre consumo de serviços através do jQuery, consulte este endereço.

Conclusão: Vimos no decorrer deste artigo algumas novidades que a Microsoft está adicionando ao WCF e ao .NET Framework, possibilitando serviços WCF se adequar melhor à serviços baseados REST e JSON, bem como facilitar o consumo destes mesmo serviços por aplicações .NET “tradicionais”.

O Framework possibilita, a Linguagem facilita

Desde a primeira versão do .NET Framework, várias funcionalidades foram construídas e as linguagens (aqui C# e VB.nET) facilitam o acesso as mesmas, tornando a vida do desenvolvedor muito mais simples. Para mostrar um exemplo simples, podemos perceber que grande parte de nós não utiliza o tipo Int32 para declarar um inteiro. Optamos pela keyword int. Grande parte de nós não utiliza o tipo Boolean para declarar um valor boleano. Optamos pela keyword bool.

Já no .NET Framework 2.0, a Microsoft introduziu no C# os iteradores, que evita a escrita explícita de classes que implementam as interfaces IEnumerable<T> e IEnumerator<T>. Tudo o que ela fez foi criar uma keyword chamada yield, que elimina toda essa necessidade, abstraindo a “complexidade” que esta implementação possui.

Para ter um exemplo mais recente, podemos avaliar as expressões lambdas. Elas nada mais são do que uma forma muito mais simples de se escrever (não de entender) delegates. Salvo a sintaxe de cada linguagem, a Microsoft está tornando tarefas complexas de serem realizadas em um modo bem mais simples de se fazer, ou seja, com novas essas keywords e um jeito diferente de se escrever o mesmo código, grande parte do trabalho complexo agora fica sob responsabilidade do compilador, que avalia e gera a saída necessária para tudo isso funcionar.

Entre as tarefas mais complexas que existem dentro do .NET Framework hoje, é a programação assíncrona. Ela não é nada trivial, pois exige um conhecimento razoável de como as coisas funcionam nos bastidores, o uso do par de métodos BeginXXX/EndXXX, das formas de capturar o resultado (callback ou poll), exceções que são disparadas, etc. Devido a essa complexidade toda, a programação assíncrona foi o alvo da Microsoft para tornar essa tarefa bem mais amena.

Apesar de isso já correr há algum tempo nos bastidores, a Microsoft anunciou hoje no PDC, um novo modelo de programação assíncrona, que permite a escrita do código assíncrono de forma tão simples quanto ao código síncrono. A partir de agora, tudo o que precisamos fazer é declarar a função como sendo assíncrona, e isso é feito através de uma nova keyword chamada async. Além disso, a função também poderá retornar um tipo predefinido que é a instância da classe Task<T>, que como o nome já sugere, representa uma tarefa que está sendo executada.

public async Task<decimal> CalcularSalarioAsync(string empregado)
{
    var resultado = await new ServicoDeSalario().CalcularAsync(empregado);

    return resultado;
}

Analisando o código acima, podemos perceber que também surgiu uma nova keyword: await. Apesar de parecer que ela irá bloquear a thread até que seja finalizado, não é isso o que acontece. Na verdade, quando o compilador encontrar essa keyword, o que ele fará é determinar que o que virá dali para baixo, até o final do método, será o callback que deverá ser disparado quando o processo assíncrono for finalizado.

A finalidade de facilitar isso, é fazer com que a programação assíncrona dentro do .NET Framework seja tão simples quanto a programação síncrona e, consequentemente, tornar as aplicações mais interativas quando falamos daquelas que exigem muitas interações de UI, ou tornando mais simples quando precisamos escrever alguma funcionalidade de forma assíncrona do lado do servidor, como é o caso de páginas/controllers assíncronos do ASP.NET e serviços em geral.

Explorarei mais detalhadamente essa funcionalidade em futuros artigos. Por agora, foi apenas um overview do que está por vir, e também mostrar que algumas complexidades são, aos poucos, absorvidas pelas linguagens. Da mesma forma que as facilidades anteriores que foram criadas dentro das linguagens, esta também será incorporada, mas vale ressaltar que continua sendo importante o seu entendimento interno da mesma forma que é importante conhecer delegates, caso contrário, expressões lambdas serão completamente estranhas.

Detectando mudanças em objetos

Há uma porção de funcionalidades dentro do .NET Framework, que podemos incorporar em nossos tipos, para enriqucê-los ainda mais em nível de comportamento. Essas funcionalidades predefinidas, já trazem recursos extremamente interessantes, que com pouco de código extra que utilizamos para “rechear” os pontos customizados, poderemos poupar muitas e muitas linhas de código se fossemos fazer isso manualmente.

Entre as várias funcionalidades que existem e que já falei bastante por aqui, uma delas é a capacidade que temos de dectectar mudanças que acontecem no estado (propriedades) das nossas classes. Geralmente as classes possuem propriedades que podem ser alteradas durante a sua execução, sendo essa alteração realizada através do bloco Set da mesma, ou através de algum método que a manipula internamente.

Por algum motivo, se quisermos detectar que alguma mudança está acontecendo ou já aconteceu, podemos implementar nesta classe as interfaces INotifyPropertyChanging e INotifyPropertyChanged que estão debaixo do namespace System.ComponentModel. Cada uma delas é utilizada para interceptar momentos diferentes, ou seja, a primeira deve ser utilizada quando queremos ser notificados antes da mudança, enquanto a segunda deverá ser utilizada para notificar quando a mudança já aconteceu.

A primeira interface, INotifyPropertyChanging, fornece um único membro, que é o evento PropertyChanging. Já a segunda, INotifyPropertyChanged, disponibiliza um outro evento chamado PropertyChanged. Ambas interfaces podem ser implementadas em classes que você deseja monitorar as alterações que podem acontecer em suas respectivas propriedades. Com isso, podemos permitir aos consumidores desta classe, serem notificados antes e depois da mudança acontecer, podendo assim tomar alguma decisão em cima disso.

Como podemos notar no código abaixo, temos uma classe chamada Cliente, que por sua vez, contém apenas uma única propriedade chamada Nome. Para qualquer evento que você crie, o ideal é criar um método que encapsule a regra para a construção dos parâmetros e o disparo dele, para evitar redundâncias pelo código. Para isso, foram criados dois métodos auxiliares, onde cada um deles encapsula a chamada para o evento que ele gerencia. Note que a atribuição do valor ao membro interno da classe, somente se dá caso o valor que chega para ela seja diferente do qual ela possui, justamente porque não faz sentido notificar alguém que a propriedade foi mudada, mas que efetivamente não foi. É importante notar também que a alteração que será feita na propriedade está envolvida pela chamada dos eventos, ou seja, antes da alteração disparamos o evento PropertyChanging, e depois que a alteração foi realizada, disparamos o evento PropertyChanged.

public class Cliente : INotifyPropertyChanging, INotifyPropertyChanged
{
    private string _nome;

    public string Nome
    {
        get
        {
            return this._nome;
        }
        set
        {
            if (value != this._nome)
            {
                this.OnPropertyChanging(“Nome”);
                this._nome = value;
                this.OnPropertyChanged(“Nome”);
            }
        }
    }

    public event PropertyChangingEventHandler PropertyChanging;

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanging(string propertyName)
    {
        if (this.PropertyChanging != null)
            this.PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
    }

    protected virtual void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

Com isso, ao construir um objeto do tipo Cliente, definimos a sua propriedade Nome como “Israel”. Em seguida, nos vinculamos aos eventos PropertyChanging e PropertyChanged, para sermos notificados caso qualquer alteração aconteça nas propriedades deste objeto. Tudo o que estamos fazendo no código abaixo, é escrevendo na tela a notificação da alteração da propriedade Nome. Finalmente, quando alteramos a propriedade Nome de “Israel” para “Israel Aece”, notamos que as mensagens de notificação aparecerão na tela.

Cliente c = new Cliente() { Nome = “Israel” };

c.PropertyChanging += 
    (sender, e) => Console.WriteLine(“Alterando a propriedade ‘{0}’.”, e.PropertyName);

c.PropertyChanged +=
    (sender, e) => Console.WriteLine(“Propriedade ‘{0}’ alterada.”, e.PropertyName);

c.Nome = “Israel Aece”;

Assim como existe essas interfaces que agregam às nossas classes, a possibilidade de serem monitoradas quando alguma mudança acontecer, também há uma nova interface chamada INotifyCollectionChanged (namespace System.Collections.Specialized), que como o nome sugere, permite adicionar à uma coleção, a possibilidade de monitoramento da mesma, onde seremos notificados quando ela for modificada, ou seja, quando elementos forem adicionados ou removidos. Essa interface fornece um único membro, que é o evento CollectionChanged.

Esse evento faz uso de um parâmetro do tipo NotifyCollectionChangedEventArgs, que expõe algumas propriedades interessantes, como por exemplo: Action, NewItems e OldItems. O primeiro deles, retorna uma das opções do enumerador NotifyCollectionChangedAction, dizendo qual foi a ação que aconteceu. Já a propriedade NewsItems, disponiliza um objeto do tipo IList, contendo os novos itens que foram adicionados, enquanto a propriedade OldItens, retorna o mesmo tipo da propriedade anterior, mas com os objetos que foram removidos da coleção.

Essa interface nos permite criar uma coleção que pode ser monitorada quando os elementos dela são manipulados. Felizmente, a Microsoft já adicionou uma coleção genérica chamada ObservableCollection<T>, que está debaixo do namespace System.Collections.ObjectModel, já implementada com essa funcionalidade.

É importante dizer que quando estamos falando no monitoramento de modificações em uma coleção, estamos atentos aos itens que ela armazena, se novos são inseridos, se outros são removidos, alterados, etc. Se uma propriedade de um objeto que está dentro dela for alterada, nada acontecerá à coleção. Como exemplo de uso, podemos visualizar o código abaixo, que cria uma instância desta coleção, definindo o tipo T como sendo Cliente. Antes de começarmos a manipular a coleção, vamos nos vincular ao evento CollectionChanged, para sermos notificados quando novos itens forem adicionados ou removidos.

static void Main(string[] args)
{
    ObservableCollection<Cliente> clientes = new ObservableCollection<Cliente>();
    clientes.CollectionChanged += ColecaoAlterada;

    clientes.Add(new Cliente() { Nome = “Israel” });
    clientes.Add(new Cliente() { Nome = “Claudia” });
    clientes.Add(new Cliente() { Nome = “Virginia” });
}

static void ColecaoAlterada(object sender, NotifyCollectionChangedEventArgs e)
{
    if (e.Action == NotifyCollectionChangedAction.Add)
        VisualizarClientes(“Cliente(s) Adicionado(s)”, e.NewItems);

    if (e.Action == NotifyCollectionChangedAction.Remove)
        VisualizarClientes(“Cliente(s) Removido(s)”, e.OldItems);
}

static void VisualizarClientes(string titulo, IList itens)
{
    if (itens != null && itens.Count > 0)
    {
        Console.WriteLine(titulo);

        foreach (var item in itens)
            Console.WriteLine(“t{0}”, item);
    }
}

Ao executar o código acima, o método ColecaoAlterada será disparado três vezes, sendo uma para cada novo cliente que está sendo adicionado à coleção. Apesar de não estar sendo utilizado aqui, a classe ObservableCollection<T> também implementa a interface INotifyPropertyChanged, que tem a finalidade de monitorar duas propriedades, sendo elas: Items e Count, que são as principais propriedades de qualquer coleção.

Observação: Inicialmente os tipos que vimos aqui para monitoramento de coleções, foram criados para atender ao Windows Presentation Foundation, e justamente por isso, também podem ser encontrados dentro do assembly WindowsBase.dll. A partir da versão 4.0 do .NET Framework, a Microsoft trouxe esses tipos para dentro do assembly System.dll, que nos permite utilizá-los por qualquer tipo de aplicação, sem a necessidade de referenciar diretamente um assembly de uma tecnologia específica.

Conclusão: Vimos no decorrer deste artigo como podemos incorporar em nossas classes a funcionalidade para detectar mudanças, recorrendo à recursos do próprio .NET Framework. Grande parte do que vimos neste artigo, serve como base para grandes funcionalidades que estão espalhados por toda a plataforma .NET, como é o caso do databinding do WPF, consumo de serviços WCF (Data Services), entre outros. A ideia aqui foi apresentar, de uma forma “crua”, essas funcionalidades, para mais tarde abordar outros recursos expostos pelo .NET Framework que fazem uso disso, e que já assumirão o conhecimento que vimos aqui.

A importância do StrongName

Imagine que você precise criar um conjunto de componentes que gerenciam a área de recursos humanos de uma determinada empresa. Entre esses componentes, vamos ter um chamado Salario, que dado o nome do funcionário, valor base do salário e a quantidade de horas extras que o funcionário realizou no mês, retorná o valor líquido que a empresa deverá pagar a ele.

Para ilustrar isso, temos um projeto do tipo Class Library, chamado de RegrasDeNegocio. Dentro deste projeto, criamos uma classe chamada Salario, que possuirá apenas o método chamado de Calcular, que receberá os parâmetros mencionados acima. Traduzindo tudo isso em código, teremos algo como:

public class Salario
{
    public decimal Calcular(string nomeDoFuncionario, decimal valorBase, decimal horasExtras)
    {
        return valorBase + horasExtras;
    }
}

Note que não há nenhuma complexidade envolvida para não desviarmos o foco. Como sabemos, este projeto dará origem à uma DLL, que poderá ser utilizada por várias aplicações que rodam dentro da empresa. Como já era de se esperar, vamos referenciá-la em um projeto chamado AplicacaoDeRH, que utilizará esse componente recém criado para calcular os salários de seus respectivos funcionários. Com isso, o código de consumo na aplicação final é algo parecido com isso:

Console.WriteLine(new Salario().Calcular(“Israel Aece”, 1000.00M, 250.00M));

Dá mesma forma que antes, vamos manter a simplicidade aqui. Depois deste software (EXE + DLL) instalado na máquina do responsável pelo RH da empresa, eu chego até o diretório físico onde ele está instalado. Copio esses dois arquivos para minha máquina para começar a analisar como esse componente e aplicação foram desenvolvidos. Como sabemos, dentro de qualquer assembly .NET, temos apenas código IL, que é um código ainda decompilável. Podemos utilizar o Reflector para isso e, consequentemente, visualizar tudo o que eles possuem (classes, métodos, parâmetros, etc.).

A partir de agora, vamos explorar a vulnerabilidade. Como eu conheço toda a estrutura que o componente (DLL) tem, nada impede de eu criar um projeto no Visual Studio .NET, com o mesmo nome, com os mesmos tipos e os mesmos parâmetros. Com isso, eu vou manipular o corpo do método Calcular, verificando se o funcionário que está sendo calculado o salário sou eu, e se for, multiplico a quantidade de horas extras por 2, para que eu possa ganhar um valor maior do que realmente deveria.

public class Salario
{
    public decimal Calcular(string nomeDoFuncionario, decimal valorBase, decimal horasExtras)
    {
        if (nomeDoFuncionario == “Israel Aece”)
            horasExtras *= 2;

        return valorBase + horasExtras;
    }
}

Depois de compilado, isso dará origem à uma – nova – DLL com o mesmo nome. De alguma forma, eu chego até o computador do responsável pelo RH da empresa, e substituo fisicamente a DLL anterior por essa DLL que acabamos de desenvolver, e que viola a regra de negócio, pois manipula o resultado para beneficiar um funcionário específico. Com isso, quando o responsável pelo RH for calcular o meu salário, ele me pagará duas vezes o valor das minhas horas extras. Se ele confia cegamente no software que dá o resultado, estou sendo beneficiado, a empresa prejudicada e dificilmente alguém encontrará o problema.

A importância do StrongName

Todo e qualquer assembly possui algumas características que ajudam ao runtime determinar a sua identidade. A identidade de qualquer assembly .NET é composta por quatro informações, a saber: nome, versão, cultura e uma chave pública. O nome nada mais é que o nome do assembly, desconsiderando a sua extensão. A versão é o número em que o projeto se encontra, e que por padrão é 1.0. Já a cultura determina se o assembly é sensitivo à alguma cultura e, finalmente, a chave pública, qual falaremos mais adiante.

Podemos utilizar várias ferramentas para conseguir visualizar essas informações. No nosso caso, se abrirmos dentro do Reflector o componente que criamos inicialmente, aquele que possui o código legal, poderemos comprovar essas características que ajudam a identificar o assembly:

RegrasDeNegocio, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

Quando criamos a assembly ilegal, com o código que multiplica a quantidade de horas extras por dois, poderemos notar que o resultado será idêntico, ou seja, não há nada que diferencie a identidade dos dois assemblies. Sendo assim, a substituição física é o suficiente para comprometer a regra de cálculo de salário, já que para a aplicação que a consome, acaba sendo o mesmo componente. Se repararmos melhor, entre as quatro informações que compõem a identidade do assembly, uma delas é chamada de PublicKeyToken, e que está nula (não definida). É justamente nela que está a solução para o nosso problema.

Dentro do .NET Framework existem dois tipos de assemblies: os que são fracamente nomeados e os que são fortemente nomeados. Aqueles que são fracamente nomeados (que é o padrão para todos os projetos .NET), são estruturalmente idênticos, possuindo o mesmo formato e os mesmos tipos definidos, mas como está, dá margem para esse tipo de problema.

Isso acontece porque quando efetuamos a referência do componente RegrasDeNegocio.dll na aplicação AplicacaoDeRH.exe, o .NET injeta no manifesto da AplicacaoDeRH, informações pertinentes ao assembly de regras de negócio. Se analisarmos o manifesto do EXE, veremos a seguinte informação:

Na imagem acima, podemos verificar que dentro da AplicacaoDeRH há uma referência para o componente RegrasDeNegocio, que inclui o nome e a versão dele. Como até então o componente ilegal possui a mesma identidade, a aplicação consome ele sem maiores problemas, não sabendo que se trata de um componente malicioso.

Para resolver o nosso problema, vamos recorrer aos assemblies fortemente nomeados (strong names). A única e principal diferença em relações aos assemblies fracamente nomeados, é a atribuição de uma chave única, que não se repetirá em nenhum lugar do mundo. A Microsoft criou esse recurso, desde a primeira versão do .NET Framework, que utiliza um par de chaves (pública e privada) para garantir a unicidade do assembly. Antes de ver como elas funcionam, vamos primeiramente entender como gerá-la. O .NET Framework fornece uma ferramenta chamada SN.exe, que é responsável por gerar e manipular essas chaves. Para gerar a nossa chave, podemos executar o seguinte comando (através do prompt do Visual Studio .NET):

SN -k MinhasChaves.snk

O arquivo gerado possui as duas chaves, a pública e a privada e você pode nomear o arquivo da forma que quiser. Com o par de chaves gerado, podemos criar um segundo arquivo para acomodar somente a chave pública, mas em um primeiro momento, isso é desnecessário. A ideia aqui é somente visualizarmos o que temos como chave pública:

SN -p MinhasChaves.snk MinhaChavePublica.snk

Para visualizarmos a chave pública, podemos utilizar o seguinte comando:

SN -tp MinhaChavePublica.snk

Public key is
0024000004800000940000000602000000240000525341310004000001000100f1589e575d9c20
cc36a0fb7245d74c8d69ddc26a0c92ebee5e65dba7c94a6583701176cc5a8fd795e11d7e366c49
a19f3ae28509fa8961e6eca103353fe98168a402dc35001b98d9d5325f6121bde11bc698f268a3
e7e338b950b565be26e371c2550dfaee54f9ef8993dc476f60b2ab5ad69d5ae832ddd7e35e43ad
6daafae2

Public key token is 0b8510fcd7fd739a

Só que o arquivo snk por si só não funciona. Você precisa vinculá-lo ao assembly qual deseja assinar. Para isso, você pode abrir o arquivo AssemblyInfo.cs, e adicionar o atributo AssemblyKeyFileAttribute, que recebe o caminho físico até o arquivo que contém o par de chaves, e que no nosso exemplo é MinhasChaves.snk.

[assembly: AssemblyKeyFile(@”C:MinhasChaves.snk”)]

Ao compilar o assembly com a chave vinculada, veremos que a identidade do assembly já mudará. Ao vincular a chave criada acima no nosso componente RegrasDeNegocio, a identidade irá aparecer da seguinte forma:

RegrasDeNegocio, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0b8510fcd7fd739a

A única e essencial diferença é que agora a propriedade PublicKeyToken reflete exatamente a nossa chave pública, que está contida no arquivo MinhasChaves.snk vinculado ao componente. A partir de agora, as aplicações que referenciarem o componente RegrasDeNegocio, guardarão além do nome e versão do mesmo, a chave pública que o identifica. Depois dessas alterações, se visualizarmos o manifesto da aplicação AplicacaoDeRH, teremos o seguinte resultado:

Com isso, qualquer pessoa maliciosa que tente refazer o assembly, por mais que ela se atente a criar toda a estrutura de tipos e métodos, definir o mesmo nome de assembly, e ainda, assinar com um outro strong name, ela jamais conseguirá reproduzir a mesma identidade e, consequentemente, não conseguirá mais alterar o componente que está instalado no cliente. É importante dizer que fisicamente, a substituição ainda poderá ocorrer, mas quando a aplicacação AplicacaoDeRH tentar acessar algum recurso do assembly RegrasDeNegocio, uma exceção será disparada, informando que o assembly solicitado não corresponde aquele que foi inicialmente referenciado.

Observação: Toda essa segurança pode continuar vulnerável se você deixar o arquivo com a chave privada em mãos erradas. Se a pessoa maliciosa conseguir ter acesso a esse arquivo, ela irá gerar o assembly idêntico como ela já fazia, mas ao invés de criar um novo par de chaves para assinar o assembly, ela utilizará o mesmo que você utilizou para assinar o seu, que é o verdadeiro, e com isso todo o problema volta a acontecer.

Para finalizar, vamos entender como todo esse mecanismo funciona e como o runtime do .NET Framework assegura isso. Quando geramos a chave a partir do utilitário SN.exe, um par de chaves é adicionado no arquivo MinhasChaves.snk. Quando compilamos o projeto com esse arquivo vinculado a ele, o .NET gera um hash do componente utilizando o algoritmo SHA1 e assina esse hash com a chave privada. O resultado deste processo é adicionado no próprio assembly, incluindo também a sua chave pública, que está matematicamente relacionada à chave privada. A imagem abaixo ilustra esse processo:

Como vimos acima, quando o componente é referenciado na aplicação que o utiliza, a chave pública também é adicionada à aplicação. Durante a execução, o .NET Framework irá aplicar o mesmo algoritmo de hash no conteúdo do componente (DLL) e dará origem à um novo hash e a chave pública embutida na aplicação que consome aquele componente, será utilizada para extrair o conteúdo (já “hasheado”) que está embutido na DLL do componente. Para determinar se a DLL é a mesma ou não, o resultado do hash deve ser igual, do contrário, a DLL foi substituída e, felizmente, uma exceção será disparada, evitando assim de consumir um componente ilegal. A imagem abaixo ilustra esse processo:

Conclusão: Ao assinar uma aplicação/componente com um strong name, podemos tirar proveito de várias funcionalidades, como por exemplo, o refinamento de segurança, a instalação no GAC, que por sua vez possibilita a centralização, execução lado a lado de múltiplas versões de um mesmo componente, etc. Mas um dos principais benefícios fornecidos por ele, é a unicidade do componente, evitando que alguém consiga reproduzí-lo e, consequentemente, colocar em risco a execução e a confiabilidade das aplicações que a consomem.

Web.config e a reinicialização da aplicação

Recentemente fui questionado sobre a possibilidade de alteração de um arquivo de configuração para a adição de uma nova string de conexão com uma determinada base de dados. Como sabemos, podemos utilizar a seção <connectionStrings /> para isso, onde elencamos todas as conexões que possam ser utilizadas pela aplicação.

O grande problema disso, é que ao modificar o arquivo Web.config, a aplicação é reinicializada, e com isso todas as informações que são armazenadas na memória são perdidas. Isso quer dizer que membros estáticos, variáveis de aplicação, de sessão e caching são descartadas, causando problemas dentro da aplicação, e trazendo péssimas experiências aos usuários.

Para contornar esse problema, podemos recorrer à uma técnica que nos permite isolar as configurações de uma seção inteira em um outro arquivo de configuração. No nosso exemplo, iremos criar um arquivo chamado connectionStrings.config contendo apenas a seção <connectionStrings />, assim como poder notar abaixo:

<connectionStrings>
  <add name=”SqlConnectionString” connectionString=”…”/>
  <add name=”OracleConnectionString” connectionString=”…”/>
</connectionStrings>

Já no arquivo de configuração da aplicação, Web.config, referenciamos este arquivo recém criado, e para isso utilizamos o atributo configSource, apontando fisicamente para ele:

<connectionStrings configSource=”connectionStrings.config” />

Só que isso somente não resolverá. Como estamos querendo evitar a reinicialização caso alguma alteração na seção <connectionStrings /> aconteça, precisamos definir o atributo restartOnExternalChanges como False, que por padrão é True. Quando é definido como False e temos essa seção em um segundo arquivo, a aplicação não será reinicializada. Essa configuração deve ser aplicada durante a criação da seção, no arquivo machine.config que está localizado dentro do diretório de instalação do .NET Framework. Abaixo temos a seção já configurada com este atributo:

<section
    name=”connectionStrings”
    type=”System.Configuration.ConnectionStringsSection, …”
    restartOnExternalChanges=”false”
    requirePermission=”false” />

Com isso, como já era de se esperar, qualquer mudança que ocorra no arquivo connectionStrings.config não provocará a reinicialização da aplicação, mas as alterações já serão detectadas por ela. O único cuidado que você precisa ter aqui é quando há alguma espécie de caching sendo utilizada pela aplicação ao pelo runtime do ASP.NET, pois as mudanças não irão refletir para a mesma até que ela seja reinicializada. Essa técnica também é válida para seções customizadas.

Novas funcionalidades do Process Explorer

A nova versão do Process Explorer, ferramenta que utilizamos para gerenciar e monitorar processos dentro do sistema operacional, passa a ter novas funcionalidades para processos gerenciados, ou melhor, para conseguirmos visualizar algumas informações relacionados exclusivamente ao .NET (CLR). A partir de agora ele é capaz de listar dos AppDomains que existem dentro da aplicação, os assemblies que foram carregados para dentro de cada um deles, contadores de performance relacionados à exceções, threading, etc. Essas informações já estavam acessíveis a partir de contadores de performance do Windows (perfmon.exe), mas agora fica muito mais fácil e intuitivo de chegar até elas. A imagem abaixo ilustra as suas duas novas abas: