O ASP.NET 2.0 inclui uma porção de novos serviços de persistência de dados em um banco de dados. Essa nova arquitetura é chamada de Provider Model e nos dá uma enorme flexibilidade, onde podemos trocar a fonte de dados/persistência e a aplicação continua trabalhando normalmente. Este provider é um módulo do software que estamos desenvolvendo que fornece uma interface genérica para uma fonte de dados, onde abstraem a base de dados. Além disso ser flexível, a qualquer momento podemos trocar a base de dados, essa arquitetura também é extensível, ou seja, se mais tarde quisermos customizar algo, podemos fazer isso sem maiores problemas.
A idéia dentro deste padrão é ter uma classe abstrata, onde dentro dela teremos métodos e propriedades que devem ser implementados nas classes concretas e, através de configurações no arquivo Web.Config, definimos a classe concreta que iremos utilizar para a aplicação. A questão é que essa classe concreta é instanciada em runtime, pois o ASP.NET recupera isso do arquivo de configuração e se encarrega de criar a instância correta da classe para uma determinada funcionalidade.
Essa arquitetura já não é lá muito nova. Se analisarmos o ASP.NET Fóruns ou até mesmo o PetShop 3.0, veremos que eles utilizam uma técnica bem parecida, onde fazem o uso dos padrões Abstract Factory e Factory Method (padrões Criacionais) para garantir essa genericidade. Isso faz com que devemos ter uma classe abstrata por trás a qual as classes concretam as implementam e o runtime se encarrega de criar a instância correta da classe concreta baseando-se nas configurações do arquivo Web.Config.
Essa arquitetura é usada extensivamente dentro do .NET Framework 2.0 (ASP.NET), onde temos classes abstratas para cada situação diferente e, para um determinado repositório de dados, uma classe concreta já implementada. Através da tabela abaixo veremos o nome da funcionalidade, a classe base e as classes concretas que se enquandram dentro do escopo do artigo:
|
Reparem que para uma determinada classe abstrata, como por exemplo MembershipProvider, já temos, por padrão, algumas classes que a Microsoft implementou para já utilizarmos a funcionalidade. Um exemplo disso é a classe SqlMembershipProvider, a qual utiliza uma base de dados SQL Server 2000 ou superior para disponibilizar o recurso. Como foi dito acima, não se restringe somente à isso. Temos classes para gerencimento de estado, Web Parts, Site Map, Profile, etc., que não foram citados/explicados por não fazerem parte do escopo deste artigo. Se no futuro precisarmos customizar alguma das funcionalidades da tabela acima para uma base de dados, como por exemplo Oracle, basta criarmos uma classe herdando de MembershipProvider ou RoleProvider e implementar os métodos e propriedades exclusivamente para aquela base de dados. Finalmente, para ilustrar essa arquitetura, é mostrado através da figura abaixo o design das classes, já com as devidas denotações de herança entre elas:
|
Figura 1 – Design das classes utilizando Provider Model. |
Além das classes que vimos acima, temos ainda duas classes estáticas (compartilhadas) que são, também, parte das principais. São estas classes que são expostas para nós, desenvolvedores, utilizarmos e fazermos a chamada aos métodos e propriedades de forma genérica. Essas classes são: Membership e Roles, as quais estão contidas dentro do Namespace System.Web.Security. No caso da classe Membership, existe um membro interno chamado s_Provider do tipo MembershipProvider (a classe base para as classes concretas de Membership, como por exemplo o SqlMembershipProvider), o qual receberá a instância da classe concreta. Essa inicialização acontece quando um método interno chamado Initialize é executado. Ele se encarrega de extrair os providers do arquivo Web.Config e instanciá-los para que, quando chamarmos os métodos e propriedades, já sejam efetivamente os métodos e propriedades da classe concreta que queremos utilizar. O funcionamento é também semelhante para a classe Roles.
Por questões de infraestrutura, utilizaremos no decorrer deste artigo o banco de dados SQL Server 2000. Mas então como prepará-lo para fazer o uso desta funcionalidade? Pois bem, para que o mesmo possa ser utilizado para isso, é necessário criarmos dentro do banco de dados a infraestrutura (Tabelas, Índices, Stored Procedures e Triggers) necessária para a utilização do recurso, que no caso serão Membership e Roles.
Quando instalamos no .NET Framework 2.0, em seu diretório %WinDir%Microsoft.NETFrameworkv2.0.50727 é instalado um utilitário chamado aspnet_regsql.exe. Este utilitário, dados alguns parâmetros, faz todo o trabalho da criação da infraestrutura dentro de um determinado banco de dados SQL Server. Veremos na tabela abaixo os parâmetros que ele aceita:
|
Abaixo é mostrado (através do prompt de comando do Visual Studio .NET 2005) alguns exemplos do uso do utilitário aspnet_regsql.exe:
|
Na primeira linha adicionamos as funcionalidades de Membership, Roles, Profile e WebEvents no banco de dados chamado “BancoDados”. Já na segunda opção adicionamos todas as funcionalidades, só que agora utilizando as credenciais do Windows. E, por último, estamos removendo toda a infraestrutura da base de dados. Quando criamos uma destas funcionalidades dentro da base de dados ele inclui uma porção de Tabelas, Stored Procedures, Triggers para que a mesma seja atendida. Se analisarmos o design da base de dados depois disso, veremos a seguinte estrutura:
|
Figura 2 – Design da base de dados para suportar as funcionalidades de Membership e Roles. |
O Arquivo ASPNETDB.MDF
Quando utilizamos Membership ou qualquer uma destas funcionalidades, se não especificarmos um provider e você não tiver um banco de dados pré-definido para o uso das mesmas, o ASP.NET cria automaticamente dentro do diretório App_Data dentro da aplicação um banco de dados chamado ASPNETDB.MDF, que faz uso do SQL Server 2005 Express Edition. Essa criação se dá quando iniciamos o WSAT – Web Site Administration Tool, onde depois de iniciarmos o provider, o arquivo MDF é criado dentro da pasta App_Data.
Com isso não seria necessário você criar a infraestrutura em uma base de dados assim como mostramos acima, pois o arquivo ASPNETDB.MDF já terá todo o schema necessário para as funcionalidades. Se o volume de usuários não é tão alto e o sistema pouco complexo, ter o ASPNETDB.MDF já resolve a necessidade. Mas quando o nível de usuários aumenta e de alguma forma você precisa fazer o relacionamento da tabela de Usuários com outras tabelas do sistema, é mais interessante ter isso unificado, ou seja, dentro de uma única base de dados.