Desenvolvimento - ASP. NET
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.
por Israel Aéce
function doClick(index, numTabs, id) {
document.all("tab" + id, index).className = "tab";
for (var i=1; i
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:
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:
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.
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:
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:
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.
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:
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); } Imports System imports System.Configuration.Provider Public MustInherit Class RefinProvider Inherits ProviderBase Public MustOverride Function Consultar(ByVal cpf As String) As Boolean End Class |
|||
C# | VB.NET |
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"]; } } } Imports System Imports System.Configuration Imports System.Configuration.Provider Public Class RefinSection Inherits ConfigurationSection <ConfigurationProperty("defaultProvider")> _ Public Property DefaultProvider() As String Get Return MyBase.Item("defaultProvider").ToString() End Get Set(ByVal value As String) MyBase.Item("defaultProvider") = value End Set End Property <ConfigurationProperty("providers")> _ Public ReadOnly Property Providers() As ProviderSettingsCollection Get Return DirectCast(MyBase.Item("providers"), ProviderSettingsCollection) End Get End Property End Class |
|||
C# | VB.NET |
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); } } Imports System.Configuration Imports System.Configuration.Provider Imports System.Web.Configuration Public NotInheritable Class Refin Private Shared _provider As RefinProvider Private Shared _providers As ProviderCollection Public Shared ReadOnly Property Provider() As RefinProvider Get Return _provider End Get End Property Public Shared ReadOnly Property Providers() As ProviderCollection Get Return _providers End Get End Property Shared Sub New() Dim section As RefinSection = _ DirectCast(ConfigurationManager.GetSection("refin"), RefinSection) _providers = New ProviderCollection() ProvidersHelper.InstantiateProviders( _ section.Providers, _ _providers, _ GetType(RefinProvider)) Dim defaultProvider As String = section.DefaultProvider If Not String.IsNullOrEmpty(defaultProvider) Then _provider = DirectCast(_providers(defaultProvider), RefinProvider) Else Throw New ConfigurationErrorsException("Provider padrão não definido.") End If End Sub Public Shared Function Consultar(ByVal cpf As String) As Boolean Return Refin.Provider.Consultar(cpf) End Function End Class |
|||
C# | VB.NET |
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> |
|||
Web.Config |
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."); } Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles Button1.Click If VBLibrary.Refin.Consultar(Me.txtCPF.Text) Then Response.Write("Há restrições financeiras para o CPF consultado.") Else Response.Write("Não há restrições financeiras para o CPF consultado.") End If End Sub |
|||
C# | VB.NET |
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:
|
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.