Utilizando o evento FirstChanceException

by Israel Aece 28. July 2011 22:58

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.");
}

Tags:

.NET Framework

O atributo InheritedExport do MEF

by Israel Aece 28. March 2011 09:24

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 { }

Tags: ,

.NET Framework

Detalhes do uso de IQueryable<T>

by Israel Aece 14. March 2011 11:51

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

Tags: , ,

.NET Framework | Data

Reportando o Progresso de Tarefas Assíncronas

by Israel Aece 4. March 2011 12:05

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.

Tags: , , ,

.NET Framework | Async

Caching do .NET Framework

by Israel Aece 2. March 2011 11:22

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:\Temp\Teste.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.

Tags: ,

.NET Framework

System.Json no .NET Framework

by Israel Aece 22. November 2010 13:23

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.

<script src="scripts/jquery-1.4.2.min.js" type="text/javascript"></script>
<script src="scripts/json2.js" type="text/javascript"></script>

<script type="text/javascript">
    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($('<option></option>')
                                .attr('value', item.Codigo)
                                .text(item.Nome));
                        });
                    }
            });
    }
</script>

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".

Tags: , ,

.NET Framework | WCF

O Framework possibilita, a Linguagem facilita

by Israel Aece 29. October 2010 00:25

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.

Tags: , ,

.NET Framework | Async | C#

Detectando mudanças em objetos

by Israel Aece 22. June 2010 12:14

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.

Tags: ,

.NET Framework | C#

Powered by BlogEngine.NET 1.5.0.0
Theme by Mads Kristensen

Sobre

Meu nome é Israel Aece e sou especialista em tecnologias de desenvolvimento Microsoft, atuando como desenvolvedor de aplicações para o mercado financeiro utilizando a plataforma .NET. [ Mais ]

Twitter

Host