Outra possibilidade de redirecionamento

Ao efetuar o redirecionamento do cliente para uma página utilizando o método Redirect da classe HttpResponse (propriedade Response da página), o ASP.NET retornará o código de status do protocolo HTTP definido como 302 que indica que o recurso foi encontrado. Se decompilar o método Redirect, veremos que ele definirá a propriedade RedirectLocation para a URL especificada informada para o método Redirect e a propriedade StatusCode estará definida como 302.

Suponhamos que um buscador tenha encontrado uma página e, dentro dela voce faz o redirecionamento para uma segunda página. O código 302 (utilizado pelo método Redirect) será retornado para o cliente, informando que a página foi encontrada e, em seguida, efetuará o redirecionamento para o endereço especificado neste mesmo método mas, e se o redirecionamento for permanente? Neste caso, podemos informar o código 301 (Moved Permanently) e ele redirecionará a requisição atual e todas as requisições subsequentes para a página definida na propriedade RedirectLocation, conforme mostrado abaixo:

Response.Status = “301 Moved Permanently”;
Response.StatusCode = 301;
Response.RedirectLocation = “OutraPagina.aspx”;
Response.End();

Criando um BuildProvider

Apesar de você não perceber, muito provavelmente já deve ter utilizado esta funcionalidade. Quando criamos arquivos de resources na nossa aplicação, automaticamente é criada uma classe chamada Resources e, dentro dela, são criadas propriedades que servem de wrapper para acessar cada um dos itens de forma mais simples e, conseqüentemente, já tendo suporte através do intellisense do Visual Studio .NET 2005. Isso é possível graças a um builder provider chamado ResourcesBuildProvider que está contido no namespace System.Web.Compilation.

Os build providers, quando adicionados no sistema de compilação do ASP.NET, nos permite customizar a compilação de alguns tipos de arquivos, como é o caso de arquivos com extensão *.resx. Sendo assim, é possível gerar uma classe dinamicamente, baseando-se em alguma regra e já podendo acessar seus membros (métodos e propriedades) durante o desenvolvimento da aplicação. Além deste exemplo, já há vários builders que a própria Microsoft criou. O trecho de código abaixo foi extraído do arquivo Web.Config global, que está no seguinte endereço: %windir%Microsoft.NETFrameworkv2.0.50727CONFIG:

<buildProviders>
    <add extension=".aspx" type="System.Web.Compilation.PageBuildProvider" />
    <add extension=".ascx" type="System.Web.Compilation.UserControlBuildProvider" />
    <add extension=".master" type="System.Web.Compilation.MasterPageBuildProvider" />
    <add extension=".asmx" type="System.Web.Compilation.WebServiceBuildProvider" />
    <add extension=".ashx" type="System.Web.Compilation.WebHandlerBuildProvider" />
    <add extension=".soap" type="System.Web.Compilation.WebServiceBuildProvider" />
    <add extension=".resx" type="System.Web.Compilation.ResXBuildProvider" />
    <add extension=".resources" type="System.Web.Compilation.ResourcesBuildProvider" />
    <add extension=".wsdl" type="System.Web.Compilation.WsdlBuildProvider" />
    <add extension=".xsd" type="System.Web.Compilation.XsdBuildProvider" />
    <add extension=".js" type="System.Web.Compilation.ForceCopyBuildProvider" />
    <add extension=".lic" type="System.Web.Compilation.IgnoreFileBuildProvider" />
    <add extension=".licx" type="System.Web.Compilation.IgnoreFileBuildProvider" />
    <add extension=".exclude" type="System.Web.Compilation.IgnoreFileBuildProvider" />
    <add extension=".refresh" type="System.Web.Compilation.IgnoreFileBuildProvider" />
    <add 
        extension=".svc" 
        type="System.ServiceModel.Activation.ServiceBuildProvider, System.ServiceModel" />
</buildProviders>

O interessante é que esta funcionalidade torna as coisas bem flexíveis e nos permite criar os nossos próprios buider providers. Para o exemplo irei utilizar um cenário bastante comum nas aplicações que desenvolvo. Para todos os valores que coloco na seção AppSettings do arquivo Web.Config da aplicação, eu costumo criar uma classe chamada Settings com propriedades de somente leitura que expõem cada um daqueles itens. Sendo assim, a idéia aqui será criar uma classe contendo propriedades que nada mais serão do que wrappers para cada um dos valores definidos na seção AppSettings.

Um detalhe importante antes de começarmos a estudar a geração do código é mencionar que tal geração deverá ser feita utilizando CodeDom (namespace System.CodeDom), que fornece tipos que representam elementos de código. Para maiores informações sobre este assunto, consulte este endereço.

O primeiro passo é criar a classe que será responsável por essa geração, que será o nosso build provider. Obrigatoriamente precisamos herdar da classe base chamada BuildProvider que está dentro do namespace System.Web.Compilation. Essa classe fornece um método chamado GenerateCode (que deverá ser sobrescrito em nosso build provider) e, como parâmetro, passa a este método uma instância do tipo AssemblyBuilder que referenciará o código gerado pelo build provider. É neste momento que precisamos recorrer ao CodeDom para gerar o código da classe proposta. Os passos são:

  • Criação de uma namespace chamado Config para organizar melhor o código a ser gerado;

  • Criar uma classe chamada Settings que armazenará as propriedades. Essa classe precisa estar dentro do namespace Config e, além disso, é necessária que seja partial para permitir que o consumidor possa “estendê-la”;

  • Finalmente, para cada item adicionado na seção AppSettings do arquivo Web.Config da aplicação, é necessário criar uma propriedade e adicionar dentro da classe Settings. Essas propriedades devem ter como nome a chave e o que ela retornará será o valor de cada item do AppSettings.

Tendo esses objetivos, vamos escrever o código que efetua essa criação. O código abaixo exibe, na íntegra, o build provider já totalmente criado.

using System;
using System.CodeDom;
using System.Configuration;
using Web = System.Web.Compilation;

public class SettingsBuildProvider : Web.BuildProvider
{
    public override void GenerateCode(Web.AssemblyBuilder assemblyBuilder)
    {
        CodeNamespace ns = new CodeNamespace("Config");
        CodeTypeDeclaration settingsClass = new CodeTypeDeclaration("Settings");
        settingsClass.IsPartial = true;
        this.GenerateProperties(settingsClass);
        ns.Types.Add(settingsClass);
        ns.Imports.Add(new CodeNamespaceImport("System.Configuration"));

        CodeCompileUnit unit = new CodeCompileUnit();
        unit.Namespaces.Add(ns);
        assemblyBuilder.AddCodeCompileUnit(this, unit);
    }

    private void GenerateProperties(CodeTypeDeclaration type)
    {
        foreach (string key in ConfigurationManager.AppSettings.AllKeys)
        {
            CodeMemberProperty prop = new CodeMemberProperty();
            prop.Name = key;
            prop.Type = new CodeTypeReference(typeof(string));
            prop.Attributes = MemberAttributes.Public | MemberAttributes.Static;
            prop.HasSet = false;

            prop.GetStatements.Add(
                new CodeMethodReturnStatement(
                    new CodeSnippetExpression(
                        string.Format(
                            "ConfigurationManager.AppSettings["{0}"]", 
                            key))));

            type.Members.Add(prop);
        }
    }
}

Analisando o código acima, dentro do método GenerateCode inicialmente criamos o namespace padrão onde a classe de configuração irá residir. A criação do namespace é realizada através do objeto CodeNamespace que, em seu construtor, podemos informar o nome do mesmo; em seguida, utilizando uma instância da classe CodeTypeDeclaration, criaremos a classe que irá expor as propriedades do arquivo Web.Config. A classe CodeTypeDeclaration fornece uma propriedade chamada IsPartial que, opcionalmente, vamos definir para true.

Com a classe devidamente criada, o próximo passo é percorrer a coleção de itens da seção AppSettings e, para cada um deles, criar uma propriedade. Esse passo é realizado pelo método privado chamado GenerateProperties. Como essas propriedades expõem valores estáticos, as mesmas também precisam ser criadas como propriedades estáticas. Para criar a propriedade, utilizamos uma instância da classe CodeMemberProperty, definimos o nome, tipo e através da propriedade GetStatements, especificamos ali o que ela retornará. Finalmente, adicionamos essa propriedade na coleção de membros do tipo (classe) que é passado como parâmetro para este método.

Para finalizar a explicação da geração do código, adicionamos a classe já completamente configurada na coleção de tipos do namespace Config que criamos anteriormente. Como essa classe dinâmica referencia a classe ConfigurationManager, é necessário importarmos o namespace System.Configuration. para dentro do namespace Config que criamos. Depois disso, criamos um objeto do tipo CodeCompileUnit, que representa um container para o CodeDom e, através da propriedade Namespaces, adicionamos o namespace que criamos. Por fim, invocamos o método AddCodeCompileUnit, passando o build provider corrente e o container de código gerado.

Uma vez que o build provider está criado, é necessário registrar o mesmo dentro da aplicação Web que o utilizará. Para isso, o ASP.NET fornece um elemento chamando buildProviders (contido dentro do elemento compilation), onde informamos o tipo (type) referente à classe geradora do código e uma extensão (extension), onde a mesma irá depositar o código gerado. Para que isso funcione bem, é necessário que dentro do diretório App_Code exista um arquivo “dummy” com a mesma extensão definida nesta seção. Como as informações a serem utilizadas para a geração do código serão extraídas do arquivo Web.Config, este arquivo “dummy” não necessita ter nenhum dado dentro. O trecho de código abaixo exemplifica o registro do build provider:

<?xml version="1.0"?>
<configuration>
    <appSettings>
        <add key="EmailParaNotificacao" value="israel@projetando.net"/>
        <add key="SiteUrl" value="http://www.projetando.net/"/>
        <add key="BlogUrl" value="http://weblogs.pontonetpt.com/israelaece/"/>
    </appSettings>
    <system.web>
        <compilation debug="true">
            <buildProviders>
                <add
                    extension=".sett"
                    type="BuildProvider.Library.SettingsBuildProvider, BuildProvider.Library"/>
            </buildProviders>
        </compilation>
    </system.web>
</configuration>

Como o cenário é gerar código (propriedades), para cada um dos itens que estão na seção AppSettings do Web.Config, ali os mesmos já estão definidos. Com esse registro feito, agora já temos suporte em tempo de desenvolvimento, permitindo que o intellisense do Visual Studio .NET 2005 já mostre as propriedades criadas. A imagem abaixo ilustra esse funcionamento:

Figura 1Intellisense já suportando as propriedades criadas dinamicamente.

O interessante é que, se você colocar um Breakpoint na linha em que faz a chamada para algumas destas propriedades, ao entrar dentro da mesma, você enxergará a classe que foi criada, com a seguinte mensagem:

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:2.0.50727.832
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace Config {
    using System.Configuration;
    
    
    public partial class Settings {
        
        public static string EmailParaNotificacao {
            get {
                return ConfigurationManager.AppSettings["EmailParaNotificacao"];
            }
        }
        
        // outras propriedades...
    }
}

Conclusão: Como pudemos ver no decorrer deste artigo, os build providers fornecem uma grande funcionalidade que, dependendo do cenário, pode reduzir consideravelmente a quantidade de código manual a ser escrita. O interessante é que você não precisa se limitar o acesso a informações do próprio ASP.NET (como foi o caso do AppSettings que utilizamos aqui). Você pode criar um arquivo qualquer, padrão XML, CSV, etc. e o teu build provider fazer o parser e, conseqüentemente, gerar o código referente para que o desenvolvedor possa acessar as informações ainda em tempo de desenvolvimento.

Exceções dentro de Threads no ASP.NET

Quando uma exceção não tratada ocorre dentro na mesma thread que está executando a requisição, provavelmente teremos aquela famosa página amarela com os detalhes do erro, incluindo a mensagem, o tipo da exceção e a stack trace (vamos levar em conta que a seção customErrors está desabilitada), continuando a aplicação funcionando perfeitamente, ou seja, outras requisições não serão comprometidas.

Exceções não tratadas que ocorrem dentro da thread que executa a requisição não traz maiores problemas para a aplicação como um todo, ao contrário quando a exceção não tratada acontece dentro de uma thread a parte que, podem ser criadas através de uma instancia da classe Thread, através do método estático QueueUserWorkItem da classe ThreadPool ou, uma forma mais esotérica, quando alguma exceção acontece dentro de um destrutor de uma classe. Dependendo do que se precisa fazer, a utilização das páginas assíncronas garantem que um processo de IO seja efetuado em uma thread a parte mas, qualquer problema que eventualmente possa acontecer, a exceção sempre será disparada sob a thread que está executando a requisição.

Neste cenário, exceções não tratadas levam o AppDomain da respectiva aplicação ASP.NET ser reciclado e, com isso, todas as informações que estão armazenadas dentro do mesmo, como Session, Application e Cache serão perdidas, fazendo com que outros usuários sejam prejudicados. Aplicações que dependem destes dados, confiantes que uma vez definido nunca será nulo, podem começar a falhar e, um dos motivos podem ser justamente a questão de exceções não tratadas em threads.

Como essas exceções não tratadas não são catalogadas mesmo quando o Health Monitoring está habilitado, temos que recorrer ao conhecido evento UnhandledException da classe AppDomain. Podemos aqui utilizar um módulo para que todas as requisições façam o uso dele e, como exemplo, este artigo mostra detalhes da implementação deste importante módulo. Apesar de existir um contador de performance que contabiliza as reinicializações da aplicação, é sempre importante efetuar o log de possíveis exceções que podem estar sendo disparadas.

Customizando o OutputCache

Um dia desses um colega me perguntou como customizar, ou melhor, como variar o OutputCaching de uma página para cada usuário da aplicação. Entre as opções que a diretiva @OutputCache fornece, temos a opção VaryByCustom, especificando como valor deste atributo uma string qualquer, com a finalidade de definirmos uma informação, que indicará como proceder para capturar a “chave” que será utilizada pelo runtime, para determinar se já existe ou não uma versão do cache. Abaixo está a configuração desta diretiva:

<%@ OutputCache Duration=”60″ VaryByCustom=”UserName” VaryByParam=”none” %>

Quando utilizamos esta técnica, devemos sobrescrever o método GetVaryByCustomString da classe HttpApplication (leia-se Global.asax) e, lá colocar o código que irá retornar a string que servirá de “chave”, para que o ASP.NET use-a para determinar se já existe ou não uma versão para ela. No nosso caso, a finalidade é criar uma versão de caching para cada usuário, portanto, temos o seguinte código:

public override string GetVaryByCustomString(HttpContext context, string custom)
{
    if(custom == “UserName” && context.User.Identity.IsAuthenticated)
        return context.User.Identity.Name;

    return base.GetVaryByCustomString(context, custom);
}

Customizando o Health Monitoring

Estou trabalhando em um projeto que utiliza o Health Monitoring para notificações de que alguma falha ocorreu na aplicação. Essa configuração faz com que qualquer exceção não tratada, seja capturada pelo Health Monitoring e a seção customErrors garante uma página amigável para o usuário final.

A maioria dos erros não tratados são ocasionados por certos valores que são passados através de algum campo no formulário e, muito raramente, através de  query strings (no caso deste projeto). A configuração padrão do Health Monitoring traz a stack trace do erro que ocorreu e outras informações relevantes ao problema, como o usuário, tipo da exceção, servidor, processo, etc. O problema é que, para simular o erro em um ambiente de teste ou até mesmo em produção, é necessário termos os mesmos valores especificados pelo usuário, para assim tornar essa simulação o mais próximo do que ocorreu. Para isso, podemos criar um evento customizado e fazer uso dele quando uma exceção for disparada.

Essa classe que representará o evento customizado herda diretamente da classe WebRequestErrorEvent e tem o nome de WebParametersRequestErrorEvent. Ao contrário dos eventos padrões que já estão embutidos no ASP.NET, os eventos customizados devem ser explicitamente disparados e, para poupar código, podemos criar um módulo. O módulo fornece a instancia da classe HttpApplication que nos permite interceptar o evento Error que, por sua vez, é disparado sempre quando um erro não tratado ocorre na aplicação. Com isso, basta simplesmente instanciar e invocar o método Raise do evento customizado.

Finalmente, apenas o que precisamos é configurar devidamente o Health Monitoring no arquivo Web.Config da aplicação, fazendo o mapeamento entre o eventMapping, rules e providers, como é mostrado detalhadamente através do código abaixo:

<system.web>
    <healthMonitoring enabled=”true” heartbeatInterval=”0″>
        <providers>
            <add
                name=”SimpleMailWebEventProvider”
                type=”System.Web.Management.SimpleMailWebEventProvider”
                from=”suporte@site.com.br
                to=”israel@site.com.br
                buffer=”false” />
        </providers>
        <rules>
            <add
                name=”WebParametersRequestErrorEventModule Mail”
                eventName=”WebParametersRequestErrorEvent”
                provider=”SimpleMailWebEventProvider”
                profile=”Default”
                minInstances=”1″
                maxLimit=”Infinite”
                minInterval=”00:00:00″/>
        </rules>
        <eventMappings>
            <add
                name=”WebParametersRequestErrorEvent”
                type=”Logger.WebParametersRequestErrorEvent, Logger”/>
        </eventMappings>
    </healthMonitoring>
    <httpModules>
        <add
            name=”WebParametersRequestErrorEventModule”
            type=”Logger.WebParametersRequestErrorEventModule, Logger”/>
    </httpModules>
<system.net>

Ao receber o e-mail, tenho todos os detalhes do erro que já era fornecido pelo evento WebRequestErrorEvent, e mais os dados contextuais da requisição:

Custom event details:
    Request – QueryStrings
            ID: 123
            Nome: Israel Aece
            E-mail: israel@projetando.net
    Request – Forms
            __VIEWSTATE: /wEPDwUJOTQ4N…m+ks3yA==
            __EVENTVALIDATION: /wEWBALs0J+…ewcwK+BGBh
            txtValor: 123,45
            txtNome: Jack Bauer
            Button1: Button

Download: WebParametersRequestErrorEvent.cs.txt (1.37 kb) e WebParametersRequestErrorEventModule.cs.txt (654.00 bytes)

Algumas novidades interessantes

A versão CTP do Visual Studio 2010 com o .NET Framework 4.0 já está disponível, assim como o Soma Somasegar disse neste post. Olhando superficialmente, há algumas funcionalidades que achei bastante interessantes, que quero listar aqui:

  • Code-snippet para HTML.
  • ClientIDMode: Nova propriedade que está contida nos controles ASP.NET, dando-nos a possibilidade de definir como renderizar o ID dos controles.
  • Gráficos: Agora temos nativamente um controle chamado Chart, mas que pode também ser utilizado em conjunto com o Visual Studio 2008 e .NET Framework 3.5. Download: Controles, VS.NET Toolbox (Add-On) e Documentação.
  • Web.config: Possibilidade de criar/transformar o seu arquivo Web.config para diversos estágios do desenvolvimento (Debug/Staging/Release) que, na maioria das vezes, as configurações mudam em cada uma dessas fases.
  • PIA – Primary Interop Assemblies: Quando adicionamos uma referencia a algum componento COM, era criado um Assembly de interoperabilidade em nosso projeto. Esse Assembly apenas contém a estrutura do componente não gerenciado e, que devemos distribuir juntamente com a aplicação onde ele foi referenciado. Com o Visual Studio 2010, podemos embutir esses tipos no Assembly da aplicação/DLL onde o componente está sendo referenciado, sem a necessidade da criação de um Assembly a parte.
  • Computação Paralela: Até então chamada de Parallel Extensions, agora ela foi incorporada dentro do .NET Framework.
  • VB.NET – Propriedades Automáticas: Assim como no C#, o VB.NET agora suporta propriedades automáticas. A sintaxe para isso é: Public Property Nome As String.
  • C# – Parametros Opcionais: Assim como nas versões do Visual Basic, o C# agora passa a suportar parametros opcionais e, assim como no VB, é necessário especificar um valor padrão. A sintaxe para isso é: public void Teste(string nome, int valor = -1){ }.
  • C# – Tipos Dinamicos: Assim como Charlie Calvert comentou aqui há algum tempo, o C# agora terá uma keyword chamada dynamic. Ao declarar uma variável como dinamica, ela suportará a chamada de membros e, a verificação se eles existem ou não, somente acontecerá em tempo de execução. Isso também é conhecido como late-binding.

Palestra TechEd 2008

Como mencionei aqui, eu palestrei no TechEd Brasil 2008, falando sobre as possíveis formas de trabalhar assincronamente dentro do ASP.NET 2.0. Eu fiz uma aplicação de exemplo para demonstrar tais funcionalidades que, estou disponibilizando aqui para download. Com relação ao PPT, eu ainda não tenho permissão para publicar e, assim que puder, eu postarei aqui o link até o mesmo.

Gostaria de agradecer publicamente a todos que estiveram presentes e, um obrigado em especial ao Rodolfo Roim e Rogério Cordeiro, que me deram esta oportunidade.

Criando novos Providers

A arquitetura chamada pela Microsoft de Provider Model foi introduzida na versão 2.0 do ASP.NET com a finalidade de podermos configurar um determinado repositório de dados, tornando isso plug and play. Há uma porção de recursos no ASP.NET 2.0 que usa essa técnica, a saber: Profile, Membership, Roles, WebParts e SiteMaps. Os Providers Models utilizam os padrões Abstract Factory e Factory Method (padrões Criacionais) para garantir a genericidade, ou seja, trabalhamos com uma classe abstrata e, em runtime, o ASP.NET se encarregará de instanciar a classe concreta.

Porém, isso não é novidade. O que poucos sabem é que podemos utilizar essa mesma arquitetura para criar nossos próprios providers, não necessariamente vinculando-os a alguma fonte de dados. A idéia deste artigo é ilustrar como devemos proceder para criar o nosso próprio modelo de providers e, tirar todo o proveito fornecido pela plataforma, ou seja, tornar a mudança entre um provider e outro de forma transparente, sem a necessidade de recompilar a aplicação.

Como cenário utilizaremos o seguinte problema: atualmente há três orgãos (Serasa, SCI e SPC) que, dado um número de CPF, retornará um valor booleano indicando se essa pessoa possui ou não restrições financeiras (ReFin). O primeiro passo é criar a classe abstrata que servirá como base para todas as classes derivadas, ou seja, teremos três tipos de consultas, pois teremos três orgãos diferentes. Essa classe base deverá obrigatoriamente herdar, também de uma outra classe abstrata, chamada ProviderBase que, por sua vez, está contida dentro do namespace System.Configuration.Provider. Essa classe fornece a implementação básica para todo e qualquer provider model.

A nossa classe chamará RefinProvider e possuirá apenas um único método abstrato para atender a nossa necessidade que é consultar um determinado número de CPF. Uma vez criada essa classe, ela deverá ser herdada por todas as classes que efetuarão a consulta. Se temos três orgãos, então teremos três classes derivadas de RefinProvider: SerasaRefinProvider, SPCRefinProvider e SCIRefinProvider. O diagrama abaixo ilustra exatamente a hierarquia dessas classes:

Figura 1 – Hierarquia das classes criadas.

Como podemos notar na imagem acima, o método Consultar da classe RefinProvider é implementado nas classes concretas onde, para cada um dos orgãos, terá uma implementação diferente, pois cada um deles exige uma maneira exclusiva de como proceder a consulta. Essas formas irão mesmo variar, e é exatamente essa a flexibilidade que o Provider Model nos proporciona, ou seja, customizar cada uma dessas classes, e apenas com uma pequena configuração no arquivo Web.Config, determinar qual será utilizada pela aplicação. O código abaixo exibe o código da classe que servirá como base para todos os providers:

using System;
using System.Configuration.Provider;

public abstract class RefinProvider : ProviderBase
{
    public abstract bool Consultar(string cpf);
}

O próximo passo é a criação da seção de configuração que devemos ter no arquivo Web.Config. Essa seção de configuração tem os mesmos princípios do membership, roles, etc., que é a possibilidade de listar todos os possíveis providers e depois decidir qual deles usar. Para a criação desta seção de configuração, precisamos criar uma classe que herde da classe abstrata ConfigurationSection, contida dentro do namespace System.Configuration, que representa uma seção no arquivo de configuração. A nossa seção terá duas propriedades: DefaultProvider e Providers; a primeira delas, do tipo string, receberá o nome do provider escolhido para ser utilizado pela aplicação; já a segunda propriedade é do tipo ProviderSettingsCollection, que representa uma coleção de objetos de configuração. O código abaixo exibe a criação desta classe e, mais tarde ainda neste artigo, veremos a sua utilização no arquivo Web.Config.

using System;
using System.Configuration;
using System.Configuration.Provider;

public class RefinSection : ConfigurationSection
{
    [ConfigurationProperty("defaultProvider")]
    public string DefaultProvider
    {
        get
        {
            return (string)base["defaultProvider"];
        }
        set
        {
            base["defaultProvider"] = value;
        }
    }

    [ConfigurationProperty("providers")]
    public ProviderSettingsCollection Providers
    {
        get
        {
            return (ProviderSettingsCollection)base["providers"];
        }
    }
}

Nota: Se alguém quiser saber um pouco mais sobre como proceder para criar uma seção de configuração customizada, consulte este artigo.

Depois da criação das classes, que são os providers, e também da classe que representará a seção de configuração, chega o momento da criação de uma das classes mais importantes, que é uma classe estática que centralizará todo o trabalho, ou seja, extrairá as informações do arquivo de configuração, criará os providers especificados e deixará a disposição da aplicação o provider padrão, ou melhor, aquele que será escolhido para que a aplicação possa usá-lo.

Essa classe receberá o nome de Refin e terá duas propriedades públicas e estáticas chamadas Provider e Providers. A primeira delas, Provider, retornará a instância do provider (RefinProvider) que está atualmente selecionado; já a segunda, Providers, retorná uma coleção do tipo ProviderCollection, contendo todos os providers que foram especificados no arquivo de configuração. Além disso, essa classe ainda possuirá um construtor estático onde, dentro dele, através do método GetSection da classe ConfigurationManager, recuperaremos a instância da seção (RefinSection) que criamos anteriormente.

Ainda dentro do construtor estático, invocamos o método estático InstantiateProviders da classe ProvidersHelper passando para o mesmo as configurações dos providers que deverão ser inicializadas, a instância de uma coleção do tipo ProviderCollection e o tipo dos providers que deverão ser inicializados. Depois que passar pelo método, o segundo parâmetro, que é a coleção, estará devidamente inicializado, ou seja, com todas as instâncias dos providers especificados no arquivo de configuração. Com a coleção definida, então utilizaremos a propriedade DefaultProvider que criamos na classe RefinSection para capturar o provider que a aplicação deve utilizar.

Finalmente, a classe Refin ainda possui, por conveniência, um método estático chamado Consultar que, em seu interior, faz a chamada para o método Consultar da classe concreta, que nada mais é do que o provider que está correntemente selecionado. O código abaixo ilustra todo esse processo que acabamos de ver:

using System;
using System.Configuration;
using System.Configuration.Provider;
using System.Web.Configuration;

public static class Refin
{
    private static readonly RefinProvider _provider;
    private static readonly ProviderCollection _providers;

    public static RefinProvider Provider
    {
        get
        {
            return _provider;
        }
    }

    public static ProviderCollection Providers
    {
        get
        {
            return _providers;
        }
    }

    static Refin()
    {
        RefinSection section =
            (RefinSection)ConfigurationManager.GetSection("refin");

        _providers = new ProviderCollection();
        ProvidersHelper.InstantiateProviders(
            section.Providers, 
            _providers, 
            typeof(RefinProvider));

        string defaultProvider = section.DefaultProvider.Trim();

        if (!string.IsNullOrEmpty(defaultProvider))
            _provider = (RefinProvider)_providers[defaultProvider];
        else
            throw new ConfigurationErrorsException("Provider padrão não definido.");
    }

    public static bool Consultar(string cpf)
    {
        return Refin.Provider.Consultar(cpf);
    }
}

Agora, depois de toda a estrutura de classes definidas, chega o momento de utilizarmos isso na aplicação Web. Como já fizemos a maior parte do código, na aplicação Web resta-nos apenas registrar a seção que criamos anteriormente e fazer o uso dela, adicionando os providers que podemos utilizar e, que neste cenário, estão sendo distribuídos juntamente com o componente que acabamos de desenvolver. O código do arquivo Web.Config fica da seguinte forma:

<?xml version="1.0"?>
<configuration>
    <configSections>
        <section name="refin" type="CSLibrary.RefinSection, CSLibrary" />
    </configSections>
    <refin defaultProvider="SCI">
        <providers>
            <add name="Serasa" type="CSLibrary.SerasaRefinProvider, CSLibrary"/>
            <add name="SPC" type="CSLibrary.SPCRefinProvider, CSLibrary"/>
            <add name="SCI" type="CSLibrary.SCIRefinProvider, CSLibrary"/>
        </providers>
    </refin>
    <system.web>
        <!-- Outras Configurações -->
    </system.web>
</configuration>

Com o código acima, conseguimos assimilar as classes que já criamos e onde elas estão sendo aplicadas neste momento. Registramos a seção RefinSection dentro do elemento configSections para mais adiante fazermos o uso dele. Recapitulando, a classe RefinSection possui duas propriedades: DefaultProvider e Providers. A propriedade DefaultProvider recebe um dos valores definidos no atributo name do elemento add dos providers. O provider que estiver ali selecionado é o que será executado, e ainda podendo ser trocado sem nenhum grande trabalho, pois basta alterar a propriedade DefaultProvider e a recompilação da aplicação não é necessária.

Para finalizar, resta-nos o código na aplicação cliente. Neste momento, utilizaremos o método estático Consultar da classe Refin. Internamente ele invoca o método da classe concreta atualmente selecionada que é exposta através da propriedade Provider. O interessante é que a aplicação nada sabe sobre o provider concreto.

protected void Button1_Click(object sender, EventArgs e)
{
    if (CSLibrary.Refin.Consultar(this.txtCPF.Text))
        Response.Write("Há restrições financeiras para o CPF consultado.");
    else
        Response.Write("Não há restrições financeiras para o CPF consultado.");
}

Como muitos estão familiarizados com as classes de providers fornecidas dentro do .NET Framework, vamos fazer uma comparação entre as classes existentes para Membership e Roles com as classes que criamos no decorrer deste artigo, para assim melhorar a compreensão e também a necessidade de cada uma das classes. Essa comparação é feita através da tabela abaixo:

Refin Membership Roles
RefinSection MembershipSection RoleManagerSection
RefinProvider MembershipProvider RoleProvider
Refin Membership Roles
SerasaRefinProvider, SPCRefinProvider e SCIRefinProvider SqlMembershipProvider e AccessMembershipProvider SqlRoleProvider e AccessRoleProvider

Conclusão: Vimos no decorrer deste artigo que a arquitetura dos Provider Models vai muito além do que a plataforma disponibiliza, que é o caso do Membership, Roles, etc.. Podemos, através dele, tornar as nossas aplicações muito mais flexíveis pois nos permite trocar o provider a qualquer momento, e ainda estender o mesmo, permitindo assim customizar para um novo orgão de consulta (neste cenário) ou para qualquer outra finalidade.

EstendendoProviders.zip (79.71 kb)

WCF, IIS e Threads

Sabemos que podemos utilizar o IIS como hosting para um serviço WCF. O Visual Studio 2008 já traz uma template chamada WCF Service. A finalidade desta template é possibilitar a criação de vários serviços, onde cada um será representado por um arquivo com a extensão *.svc. Depois de desenvolvido, podemos publicar este serviço em um servidor com IIS e com o .NET Framework devidamente instalado.

O WCF fornece dois tipos não documentados dentro do namespace System.ServiceModel.Activation, a saber: HttpHandler e HttpModule. A finalidade destes tipos é acoplá-los ao pipeline do ASP.NET e, quando uma requisição chegar para um serviço WCF (*.svc), o ASP.NET irá desviar a execução para o HttpHandler.

Um grande problema que ocorre neste procedimento é com relação ao uso de threads. Como a requisição é inicialmente passada pelo ASP.NET e, quando ele detecta que se trata de um serviço WCF, uma thread de I/O é criada para executar o serviço mas, a worker thread que é utilizada por tratar as requisições do ASP.NET, continuará bloqueada até que a requisição à operação esteja completa. Isso impedirá que a worker thread atenda à outras requisições enquanto espera que o processo seja finalizado.

A configuração padrão do WCF (mesmo depois do SP1 instalado) continua utilizando esta mesma técnica, ou seja, o processamento síncrono da operação. Mas, a partir do SP1 do .NET Framework 3.5, foram introduzidos mais dois tipos que tem o papel de executar, de forma assíncrona, as operações que chegam pelo ASP.NET ao WCF. Esses novos tipos são: ServiceHttpModule e ServiceHttpHandlerFactory.

Com estes dois novos tipos acoplados no pipeline do ASP.NET, permitirá que a execução seja feita de forma assíncrona, liberando a worker thread utilizada pelo ASP.NET para atender a outras requisições. Para habilitar o processamento assíncrono, podemos utilizar uma ferramenta chamada WcfAsyncWebUtil.exe, desenvolvida pelo Wenlong Dong.

É importante dizer que há uma relação entre o processamento assíncrono do runtime do ASP.NET/WCF com o contrato assíncrono (AsyncPattern = true). Se combinarmos o handler/módulo assíncrono com o contrato assíncrono, nenhuma thread será consumida durante a espera pelo processamento assíncrono da operação. Mesmo quando o contrato não é assíncrono, ao utilizar o handler/módulo assíncrono, teríamos uma diminuição de threads rodando concorrentemente e assim, tendo um ganho considerável.

TechEd Brasil 2008

Bem, como todo mundo já sabe, de 14 à 16 de outubro acontece em São Paulo o TechEd Brasil e, como no ano passado, este ano também estarei efetuando uma palestra na sala 7 no dia 15/10, das 11:00 às 12:15hs. O tema da palestra é:

WEB401 – Programação Assíncrona do ASP.NET 2.0
Nível: 400

Um dos principais pontos para a implementação de aplicações ASP.NET escaláveis, é o uso de pool de threads do ASP.NET da maneira mais eficiente possível. Isto significa evitar situações onde threads fiquem: aguardando o retorno de consultas à base de dados, chamadas à Web Services e que operações de I/O terminem. Nesta sessão descreveremos como ajustar os três modelos de programação assíncrona em ASP.NET. Se você está preocupado em construir sites Web com alta escalabilidade assista essa sessão.

Pré-Requisitos: Como pré-requisito para esta palestra é importante que o ouvinte tenha conhecimento nas classes de acesso à banco dados, uso de proxy para chamadas à Web Services e, se possível, familiaridade com o modelo de programação assíncrona (APM) do .NET Framework (Begin/End).

Além da palestra, estarei também participando do Ask The Experts, que é um bate-papo informal sobre determinadas tecnologias. Estarei na seção onde será abordado sobre ASP.NET, AJAX, Silverlight e Windows Live. Para aqueles que estiverem no evento e, se estiver disposto a assistir a palestra ou discutir sobre alguma tecnologia, fiquem a vontade para me procurar e, conseqüentemente, conversar à respeito dos assuntos relacionados a palestra e/ou algo sobre .NET.