ASP.NET Profile – Parte 2

Como já foi dito anteriormente, os Profiles, por default, são armazenados em um banco do dados do tipo Microsoft Access dentro da pasta App_Data da aplicação. Haverá casos, onde temos a nosso dispor, uma base de dados SQL Server, que é infinitamente mais robusta que um simples arquivo MDB do Microsoft Access, e com isso podemos utilizá-la para que a aplicação grave neste servidor SQL Server as informações/dados referentes aos Profiles da mesma.

Para que isso seja possível, temos no ASP.NET o que chamamos de um Profile Provider, que permitirá você especificar onde as informações serão salvas, definindo isso em seu arquivo de configuração (Web.Config ou Machine.Config).

O ASP.NET nos fornece, já intrinsicamente, um provider/classe chamado SqlProfileProvider. Esta classe deriva de uma classe base chamada ProfileProvider, ficando assim flexível para quando você precisar criar seu próprio Profile Provider para um banco de dados específico, como por exemplo MySQL, Oracle, SyBase, etc.

Mas antes de utilizarmos o SqlProfileProvider temos que fazer algumas configurações fora da nossa aplicação, ou seja, rodarmos um aplicativo “*.exe” que vem juntamente com o .NET Framework 2.0, chamado aspnet_regsql, que você poderá encontrá-lo no seguinte path: WindowsMicrosoft.NETFramework[versao]. Este processo se faz necessário porque através dele, é que são geradas as Stored Procedures dentro do servidor SQL Server. Stored Procedures que são executadas pelo SqlProfileProvider para a manipulação dos dados dos Profiles.

Feito este processo, o que se tem a fazer agora é configurar o Provider, no nosso caso, o SqlProfileProvider no arquivo Web.Config da aplicação. Abaixo um trecho do código do arquivo Web.Config onde é configurado o SqlProfileProvider a nível da nossa aplicação (se desejar definir este Provider para todas as aplicações, poderá fazer esta configuração no arquivo Machine.Config):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 
<configuration>
    <system.web>
        <connectionStrings>
            <add name=”CNProfiles”
                connectionString=”Server=localhost;Trusted_Connection=true;database=DBProfiles” />
        </connectionStrings>
 
        <authentication mode=”Forms” />
        <anonymousIdentification enabled=”true” />
        <profile defaultProvider=”SQLProvider” />
 
        <providers>
            <add name=”SQLProvider”
                type=”System.Web.Profile.SqlProfileProvider”
                connectionStringName=”CNProfiles” />
        </providers>
 
        <profile>
            <properties>
                <add name=”Nome” defaultValue=”” allowAnonymous=”true” />
                <add name=”Email” defaultValue=”” allowAnonymous=”true” />
            </properties>
        </profile>
    </system.web>
</configuration>
 
Código 1 – Configurando o Profile no arquivo Web.Config.

Como vemos, dentro do elemento providers temos o nome e o tipo (a classe) que no nosso caso é a SqlProfileProvider que fará o trabalho para gerir os Profiles dentro do SQL Server. Temos também um atributo chamado connectionStringName onde indicamos qual é a string de conexão, que informará o servidor e o banco de dados que será utilizado. Caso você tenha customizado uma classe para um banco de dados qualquer, o tipo (type) mudará, onde voce deverá informar o tipo da classe criada, inclusive o Namespace.

Agora basta utilizar os Profiles normalmente, como foi explicado na primeira parte deste artigo, e os dados já serão gravados dentro do SQL Server especificado na Connection String do arquivo Web.Config.

Gerenciando Profiles – Relatórios

Temos uma classe chamada ProfileManager que nos permite gerenciar os Profiles da nossa aplicação. É através desta classe, que nos é fornecido uma série de métodos e propriedades estáticos para as tarefas mais comuns, como por exemplo excluir, exibir, recuperar Profiles, etc.

Com isso, podemos criar uma aplicação Console ou mesmo um Windows Service que gerencie os Profiles da nossa aplicação, colocando-lá todas as manipulações que desejamos fazer com os Profiles. Abaixo os métodos e propriedades da classe ProfileManager:

Membro Descrição
 DeleteInactiveProfiles Exclui os Profiles inativos, baseando-se em uma data que lhe é informada.
 DeleteProfile Exclui um Profile específico baseando-se no username.
 DeleteProfiles Exclui vários Profiles baseando-se em um Array de usernames.
 FindInactiveProfilesByUserName Retorna uma coleção de objetos do tipo ProfileInfo que estão inativos desde uma data e username específico.
 FindProfilesByUserName Retorna uma coleção de objetos do tipo ProfileInfo de acordo com um username específico.
 GetAllInactiveProfiles Retorna uma coleção de objetos do tipo ProfileInfo com Profiles inativos baseando-se em uma data.
 GetAllProfiles Retorna uma coleção de objetos do tipo ProfileInfo com todos os Profiles.
 GetNumberOfInactiveProfiles Retorna um número inteiro que indica a quantidade de Profiles inativos baseando-se em uma data específica.
 GetNumberOfProfiles Retorna um número inteiro que indica a quantidade de Profiles.
 ApplicationName Nome da aplicação que armazena o Profile.
 AutomaticSaveEnabled Indica que o Profile será salvo automaticamente no final da execução da página.
 Enabled Resgata um valor booleano que indica se o Profile está ou não ativo na aplicação.
 Provider Resgata o provider padrão da aplicação.
 Providers Resgata uma coleção de providers de uma aplicação.

Ainda temos um objeto bastante importante chamado ProfileInfo, que contém informações de um Profile específico. Veremos na tabela abaixo as propriedades desta classe:

Membro Descrição
 IsAnonymous Indica se é ou não um Profile anônimo.
 LastActivityDate Indica última data que o Profile foi acessado.
 LastUpdatedDate Indica última data que o Profile foi atualizado.
 Size Resgata o tamanho do Profile.
 UserName Resgata o username do Profile.

Abaixo um exemplo de como utilizar a classe ProfileManager dentro de uma aplicação qualquer:

1
2
3
4
 
Sub Page_Load()
    Me.GridView1.DataSource = ProfileManager.GetAllProfiles(ProfileAuthenticationOption.All)
    Me.GridView1.DataBind()
End Sub
 
Código 2 – Utilizando a classe ProfileManager.

Como o método GetAllProfiles retorna uma coleção de objetos do tipo ProfileInfo, podemos tranquilamente definí-lo como DataSource de um container de dados para exibi-lo para o usuário assim como é mostrado no código 2, logo acima.

CONCLUSÃO: Nesta situação, os providers e os Profiles são bastante úteis, onde é encapsulado uma porção de códigos para nos ajudar a gerenciar essas informações que temos em aplicações Web/ASP.NET. Claro que se quiser utilizar uma outra base de dados, não SQL Server, terá mesmo que customizar um Provider para esta, herdando da classe base ProfileProvider. E com o uso da classe ProfileManager, temos acesso e gerenciamento dos Profiles da nossa aplicação, evitando trabalhar diretamente com código SQL.

Validation Groups

O problema é quando pressionamos qualquer um destes botões, todos os validadores são disparados, impossibilitando o usuário de continuar o processo, obrigando-o a preencher todo o formulário para prosseguir.

Isso foi pensado melhor e resolvido na versão 2.0 do ASP.NET e a Microsoft incluio os chamados Validation Groups que é exatamente o tema deste artigo. Como podemos ver na Figura 1 logo abaixo, o problema que acontece na versão 1.x, todos os validators são disparados, mesmo os que supostamente não deveriam ocorrer.

Figura 1 – O problema dos validadores da versão 1.x.

Como podemos reparar, quando pressionamos qualquer um dos botões, o formulário como um todo é tratato, e todos os validadores onde a consistência falha, são disparados.

Agora os controles tem uma nova propriedade, chamada ValidationGroup que receberá uma string que identificará o “Grupo” a ser validado pelo botão que desejar, e assim, tratar independentemente “ilhas” de controles nos formulários. Abaixo veremos o código HTML que exemplifica o uso dos Validation Groups:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
 
 
Nome 1:
<asp:TextBox
    id=”TextBox1″
    runat=”server”>
</asp:TextBox>
 
<asp:RequiredFieldValidator
    id=”RequiredFieldValidator1″
    runat=”server”
    ErrorMessage=”Digite o Nome 1″
    ControlToValidate=”TextBox1″
    SetFocusOnError=”True”
    ValidationGroup=”Grupo1″>Digite o Nome 1</asp:RequiredFieldValidator>
 
<asp:Button
    id=”Button1″
    runat=”server”
    Text=”Grupo 1″
    ValidationGroup=”Grupo1″>
</asp:Button>
 
<HR>
 
Nome 2:
<asp:TextBox
    id=”Textbox2″
    runat=”server”>
</asp:TextBox>
 
<asp:RequiredFieldValidator
    id=”Requiredfieldvalidator2″
    runat=”server”
    ErrorMessage=”Digite o Nome 2″
    ControlToValidate=”TextBox2″
    SetFocusOnError=”True”
    ValidationGroup=”Grupo2″>Digite o Nome 2</asp:RequiredFieldValidator>
 
<asp:Button
    id=”Button2″
    runat=”server”
    Text=”Grupo 2″
    ValidationGroup=”Grupo2″>
</asp:Button>
 
Código 1 – Definindo o ValidationGroup.

Podemos reparar que para “amarrar” os controles, inclusive os validadores à serem validados por um botão qualquer, definimos uma string que baseado nela, o botão apenas verificará a consistência dos validadores que também são do mesmo grupo.

Outra característica da versão 2.0 do ASP.NET, é a propriedade SetFocusOnError. Quando esta propriedade esta definida como True e a consistência desse validador falhar, o foco é direcionado automaticamente para o controle que está associado ao mesmo.

CONCLUSÃO: Os validadores são uma forma elegante de tratarmos formulários em aplicações ASP.NET evitando assim, códigos Javascript que seríamos obrigados a digitar para obter o mesmo resultado. Apesar disso, na versão 1.x tínhamos o problema que vimos acima, e que, nesta nova versão foi solucionado com a adição da propriedade ValidationGroup.

Compressão de Arquivos

Já agora nesta nova versão do .NET Framework, 2.0, temos um novo namespace, dentro do System.IO, chamado System.IO.Compression. Este namespace contém 2 classes chamadas DeflateStream e GZipStream. Elas tem uma interface semelhante, mas uma delas, a DeflateStream utiliza o algoritmo Deflate para comprimir e descomprimir os arquivos. Temos também um enumerador dentro deste namespace chamado CompressionMode, que indica o que será feito (Compressão ou Descompressão) com o Stream.

Para ilustrarmos isso, vamos fazer um teste utilizando a classe GZipStream, comprimindo um arquivo de 527 KB, analisando os códigos abaixo:

1
2
 
Imports System.IO
Imports System.IO.Compression
 
Código 1 – Importando os Namespaces necessários.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
 
Public Sub Comprime(ByVal arquivo As String)
    Dim stream As FileStream = Nothing
    Try
        stream = New FileStream(arquivo, FileMode.Open, FileAccess.Read, FileShare.Read)
 
        Dim buffer(stream.Length – 1) As Byte
        Dim count As Integer = stream.Read(buffer, 0, buffer.Length)
        stream.Close()
 
        Dim ms As New MemoryStream()
        Dim zip As New GZipStream(ms, CompressionMode.Compress, True)
        zip.Write(buffer, 0, buffer.Length)
        zip.Close()
        WriteFile(“ArquivoCompact.gz”, ms.ToArray, ms.Length)
    Finally
        If Not (stream Is Nothing) Then
            stream.Close()
        End If
    End Try
End Sub
 
Public Sub Descomprime(ByVal arquivo As String)
    Dim stream As FileStream = Nothing
    Try
        stream = New FileStream(arquivo, FileMode.Open, FileAccess.Read, FileShare.Read)
        Dim ms As New MemoryStream(stream.Length)
        Dim unzip As New GZipStream(ms, CompressionMode.Decompress, True)
 
        Dim data(stream.Length – 1) As Byte
        stream.Read(data, 0, data.Length)
        ms.Write(data, 0, data.Length)
        ms.Position = 0
 
        Dim dataOutput() As Byte = ReadBytes(unzip)
        WriteFile(“ArquivoOriginal.exe”, dataOutput, dataOutput.Length)
        unzip.Close()
    Finally
        If Not (stream Is Nothing) Then
            stream.Close()
        End If
    End Try
End Sub
 
Public Function ReadBytes(ByVal s As Stream) As Byte()
    Dim buffer(10000) As Byte
    Dim bytesLidos As Integer = 0
    Using ms As New MemoryStream()
        Do
            bytesLidos = s.Read(buffer, 0, 10000)
            ms.Write(buffer, 0, bytesLidos)
        Loop Until bytesLidos <= 0
        Return ms.ToArray()
    End Using
End Function
 
Código 2 – Função que comprime de descomprime o arquivo informado.

Como podemos ver no código 2 logo acima, utilizamos a classe GZipStream tanto para comprimir quanto para descomprimir o arquivo do nosso exemplo. Para o nome do arquivo que é passado para a função “Comprime”, resgatamos o mesmo para um FileStream. Com isso, conseguimos manipulá-lo e passarmos para a classe de compressão fazer o trabalho e gerar o arquivo em menor tamanho. Com um objeto do tipo MemoryStream, recuperamos o conteúdo que é gerado pelo GZipStream e assim salvamos/criamos o arquivo com extensão “*.gz”, já comprimido.

O Método WriteFile tem apenas a finalidade de gerar os arquivos fisicamente no disco, dado um nome do arquivo e o conteúdo que será salvo para este. Abaixo o código deste método para análise:

1
2
3
4
5
6
7
8
 
Public Sub WriteFile(ByVal fileName As String, _
    ByVal data() As Byte, _
    ByVal count As Integer)
 
    Dim f As New FileStream(fileName, FileMode.Create, FileAccess.Write)
    f.Write(data, 0, count)
    f.Close()
End Sub
 
Código 3 – Método auxiliar que gera os arquivos fisicamente.

Agora já analisando a outra parte do código 2, ou melhor, o método “Descomprime”, ele faz basicamente da mesma forma que anteriormente, ou seja, carrega o arquivo comprimido para um FileStream e depois passa a instância de um MemoryStream para a classe fazer o trabalho de descompressão do arquivo informado. Um método auxiliar, chamado “ReadBytes”, é criado para recuperarmos o conteúdo extraído/original do arquivo através do GZipStream.

Depois de executar o código, o arquivo EXE de 527 KB, diminuiu para 225 KB, e com certeza é bem considerável.

Utilizando a Compreensão no ASP.NET

Mas claro que a única utilidade deste namespace não é somente para comprimir e descomprimir arquivos em disco. No ASP.NET, ele terá um papel bastante importante, ou seja, poderemos através dele, comprimirmos o conteúdo/output de páginas ASPX antes de enviar ao cliente (clientes que suportam este tipo de compressão), para termos uma melhor performance e consequentemente diminuimos o tráfego de dados na rede.

Para aqueles que utilizam IIS 6.0, pode ir até as propriedades da aplicação, na Tab “Service” e marcar a compressão de arquivos, já que a mesma, por padrão, vem desabilitada e assim não seria necessário nenhuma espécie de programação, pois o IIS cuidaria disso.

Mas há casos em que não temos o IIS 6.0 ou o acesso a ele. Isso ocorre quando temos a nossa aplicação hospedada em um host qualquer, e em quase 100% dos casos, o cliente não tem acesso ao IIS. Nesta situação é que o ASP.NET 2.0 + .NET Framework 2.0 nos ajudam, pois podemos comprimir os “arquivos” programaticamente através de HttpModules e fazendo também o uso das classes de compressão que vimos no início do artigo.

Chamo a atenção aqui que é extremamente necessário que o cliente tenha habilitado no seu browser o suporte ao HTTP 1.1, pois a compressão é uma “feature” disponibilizada nesta versão. Caso ela não esteja configurada, então o processo não funcionará. Inclusive você poderá identificar isso programaticamente no próprio ASP.NET, ou mesmo, através do Trace.

Para habilitar o suporte ao HTTP 1.1 no seu browser (Internet Explorer), que por padrão já vem habilitado, deve-se ir até o menu Tools (Ferramentas), Internet Options (Opções da Internet), tab Advanced (Avançado), e habilitar a opção “Use HTTP 1.1”.

Para o nosso teste, infelizmente não tenho no momento ferramentas de Monitoring (como por exemplo a Network Monitor Tools, disponibilizada no Windows 2003 server), justamente por estar desenvolvendo este artigo em Windows XP Professional. Apenas para exemplo, vamos verificar através do Trace da página qual dos tipos de compressão foi usado. Vale lembrar que isso não diminui o tamanho final do output da página, ou seja, a finalidade é que o servidor compacta o retorno e envia ao cliente, e este, por sua vez, recebe este conteúdo e automaticamente descompacta e exibe ao usuário e por esta questão é que temos que ter o suporte ao HTTP 1.1.

O projeto constituirá em um Class Library que criaremos uma classe que implementará a interface IHttpModule. Esse processo é bem semelhante o que já fazemos atualmente, ou seja, no evento Init anexamos o procedimento que será disparado quando o evento BeginRequest for invocado. A diferença está na codificação deste procedimento, onde será nele que verificaremos se a requisição que solicitou a página suporta a compressão e assim compactamos o retorno de acordo com os algoritmos suportados pelos browser. Veremos abaixo o componente já implementado:

1
2
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
 
Imports Microsoft.VisualBasic
Imports System.IO
Imports System.IO.Compression
Imports System.Web
 
Public Class Compress
 
    Implements IHttpModule
 
    Public Sub Dispose() Implements System.Web.IHttpModule.Dispose
    End Sub
 
    Public Sub Init(ByVal context As HttpApplication) Implements System.Web.IHttpModule.Init
        AddHandler context.BeginRequest, AddressOf BeginRequest
    End Sub
 
    Private Sub BeginRequest(ByVal sender As Object, ByVal e As EventArgs)
        Dim app As HttpApplication = DirectCast(sender, HttpApplication)
        Dim headers As String = app.Request.Headers.Get(“Accept-Encoding”)
        If Not headers = String.Empty Then
            Dim stream As Stream = app.Response.Filter
            If headers.ToUpper.IndexOf(“GZIP”) > -1 Then
                app.Response.Filter = New GZipStream(stream, CompressionMode.Compress)
                app.Response.AppendHeader(“Content-Encoding”, “gzip”)
                app.Context.Trace.Warn(“Compressao via GZIP”)
            ElseIf headers.ToUpper.IndexOf(“DEFLATE”) > -1 Then
                app.Response.Filter = New DeflateStream(stream, CompressionMode.Compress)
                app.Response.AppendHeader(“Content-Encoding”, “deflate”)
                app.Context.Trace.Warn(“Compressao via DeflateStream”)
            End If
        End If
    End Sub
 
End Class
 
Código 4 – Implementando a interface IHttpModule para compreesão.

Analisando o código acima, no procedimento BeginRequest recuperamos a instância do objeto HttpApplication e com este, fazemos as devidas consistências, recuperamos e definimos o tipo de retorno. Através do método Get que é fornecida pela propriedade Headers (que contém o cabeçalho da requisição HTTP), recuperamos o Accept-Encoding, que nos informará se o cliente tem ou não suporte aos tipos de compactação do .NET (GZipStream e DeflateStream).

Baseado neste valor, criamos a instância do “compactador” que será utilizado, e com isso, atribuímos ao construtor da classe responsável pela compactação o Stream que é devolvido para o cliente, justamente para internamente, ela criar a versão compactada desta página e devolver para o cliente.

Nesta altura o componente já está criado e com a DLL gerada. Temos agora que adicionar a referência ao nosso projeto ASP.NET Application. Depois de feita a referência, temos que, no arquivo Web.Config desta aplicação ASP.NET, adicionar o HttpModule para que ele possa ser executado nas requisições das páginas.

No exemplo deste artigo, o projeto Class Library foi chamado de “Component” e a classe responsável pela compressão de “Compress”. Então adicionamos este módulo no arquivo Web.Config da aplicação, assim como já fazíamos anteriormente, ou seja, dentro da seção “<system.web></system.web>”, como é mostrado abaixo:

1
2
3
 
<httpModules>
    <add name=”Compress” type=”Component.Compress, Component”/>
</httpModules>
 
Código 5 – Configurando o Módulo no arquivo Web.Config.

Depois disso, ao rodar a aplicação a compactação passa a ser realizada pelo ASP.NET, assim como podemos ver na Figura 1 logo abaixo onde o Trace nos aponta qual dos “compactadores” foi utilizado.

Figura 1 – Verificando qual compactador foi utilizado.

CONCLUSÃO: Através destas classes agora disponíveis nesta nova versão do .NET Framework, podemos criar diversas funcionalidades para as nossas aplicações, sem a necessidade de adquirir um componente de terceiros. Este novo namespace, System.IO.Compression fornece todo o suporte necessário para atingir os objetivos quando se trata de compressão e descompressão de arquivos em .NET.

Nova arquitetura de Segurança

Ao contrário do ASP.NET 1.x, o ASP.NET 2.0 nos fornece uma grande infraestrutura, incluindo classes e controles, para trabalharmos com segurança e gerencimento de usuários das nossas aplicações Web. Nas versões anteriores, tínhamos que construir nossas próprias classes para gerir os usuários da nossa aplicação em nossa Base de Dados ou mesmo no Active Directory e, sendo assim, tínhamos que escrever muito mais códigos para atingirmos este nosso objetivo, isso ainda sem levar em consideração a parte de designer (ASPX).

Na versão 2.0 do ASP.NET, com a grande gama de controles a nosso dispor para melhor trabalharmos o front-end (UI), temos ainda classes para manipular os usuários em nossa Base de Dados. Classes quais utilizam o Provider Model Design Pattern, e assim conseguímos ter um código genérico, idenpendentemente de que Banco de Dados (ou qualquer outro tipo de repositório) estamos utilizando em nossa aplicação.

A Infraestrutura

Assim como o Profile, o ASP.NET também utiliza o Providers para gerenciar a segurança, sendo eles, dois providers: MembershipProvider e o RoleProvider. O MembershipProvider é utilizado para gerir os usuários (armazenando, criando, excluindo e alterando) e passwords. O RoleProvider é utilizado para gerenciar as Roles (Regras) dos usuários dentro da aplicação.

Para versão Beta 1, o Provider padrão é o AccessMembershipProvider. Como o Microsoft Access não suporta um grande volume de usuários “pendurados”, ele foi substituído pelo SqlMembershipProvider na versão Beta 2 do Visual Studio .NET 2005, e como o próprio nome diz, é voltado para o Banco de Dados SQL Server. Se ainda há alguém que pretende utilizar o AccessMembershipProvider, é possível encontrá-lo neste endereço.

Os dois providers são descendentes de uma mesma classe abstrata, chamada MemberShipProvider, deixando assim, flexível para extender a funcionalidade para outros tipos de Banco de Dados, como por exemplo Oracle, Sybase, ou até mesmo arquivos XML, bastando apenas herdar da classe base, MemberShipProvider, e implementar os métodos e propriedades para a Base de Dados que deseja utilizar.

Para utilizar o SqlMembershipProvider, é necessário termos dentro do SQL Server os objetos (Tabelas e Stored Procedures) necessários para que seja possível o uso deste Provider. Junto ao Microsoft .NET que é instalado na máquina, existe uma ferramenta chamada “aspnet_regsql.exe”, que faz todo este processo automaticamente, onde apenas precisamos informar o servidor e o Banco de Dados onde serão instalados estes objetos.

Depois do provider escolhido, ainda é necessário definirmos no arquivo Web.Config qual será o provider a ser utilizado. Abaixo um exemplo de como definí-lo:

1
2
3
4
5
 
<configuration>
    <system.web>
        <membership defaultProvider=”AspNetSqlProvider” />
    </system.web>
</configuration>
 
Código 1 – Definindo a estrutura do Provider de segurança no arquivo Web.Config.

Como vemos, no atributo defaultProvider definimos o nome do provider que utilizaremos na aplicação. Este provider está definido dentro da coleção de providers que podemos também, definir dentro do arquivo de configuração (Machine.Config ou Web.Config) os providers que podemos ou não utilizar.

Quando criamos o nosso próprio provider, precisamos registrá-lo na coleção de providers e assim, definimos uma séria de atributos, sendo um deles o type, que informará a classe que será utilizada. Nada impedirá de ter mais de um provider definido, mas obrigatoriamente, no atributo defaultProvider, devemos especificar o provider que iremos utilizar na aplicação. Abaixo temos uma lista completa:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 
<configuration>
    <system.web>
        <membership defaultProvider=”AspNetSqlProvider”>
            <providers>
                <add name=”AspNetSqlProvider”
                    type=”System.Web.Security.SqlMembershipProvider”
                    connectionStringName=”Connection_String”
                    enablePasswordRetrieval=”false”
                    enablePasswordReset=”true”
                    requiresQuestionAndAnswer=”true”
                    passwordFormat=”Hashed”
                    applicationName=”/” />
            </providers>
        </membership>
    </system.web>
</configuration>
 
Código 2 – Configurando os providers.

 

Atributo Descrição
name Nome do Provider.
type Tipo do objeto, incluindo o Namespace.
connectionStringName Connection String com a Base de Dados.
enablePasswordRetrieval Possibilita ou não recuperar o password de um usuário.
enablePasswordReset Possibilita ou não que usuários possam reiniciar seu password com um outro aleatório.
requiresQuestionAndAnswer Indica se o usuário deverá ou não responder a questão antes de recuperar ou reiniciar o seu password.
passwordFormat Opções: Clear, Encrypted e Hashed.
applicationName Nome da Aplicação. Utilizado quando você precisa isolar os usuários em aplicações diferentes, utilizando esta mesma estrutura.

Nota: É importante mencionar que dentro do .NET Framework, também já temos implementada a classe ActiveDirectoryMembershipProvider, que faz o mesmo trabalho, mas agora, utilizando o Active Directory como repositório.

Os Controles

O ASP.NET 2.0 contém um conjunto completo de controles para trabalharmos com a segurança, contendo inclusive uma Tab chamada Security dentro da ToolBox do Visual Studio .NET 2005 para armazenar estes controles. Vamos neste artigo, analisar superficialmente alguns destes novos controles, como por exemplo o ChangePassword, CreateUser, LogIn, LoginView e o PasswordRecovery. Estes controles também tem a grande vantagem de trabalharem diretamente com o Provider que definimos no arquivo Web.Config, e assim, faz todo o processo, inclusive as queries necessárias para manipular os dados na Base de Dados.

ChangePassword

Figura 1 – Controle que permite a mudança de password.

O controle ChangePassword tem a finalidade de alterar o password de um determinado usuário, onde temos propriedades para configurar, como por exemplo a propriedade MailDefinition que permite você definir um email para quando o usuário alterar o password, uma mensagem é enviada a ele informando a mudança e o seu novo password.

CreateUser

Figura 2 – Controle que permite a criação de novos usuários.

Este controle, do tipo Wizard, permite a criação de novos usuários dentro na Base de Dados, permitindo também o envio de um email de confirmação ao usuário que foi cadastrado. Este controle também possui uma propriedade chamada AutoGeneratePassword, que é responsável por gerar um password randômico para o novo usuário.

LogIn

Figura 3 – Controle para efetuar o Login.

Como podemos perceber, este controle, dado um nome de usuário e password, possibilita-nos a verificar se um determinado usuário encontra-se ou não cadastrado em nossa Base de Dados. Temos também uma propriedade interessante neste controle, chamada VisibleWhenLoggedIn. Esta por sua vez, recebe um valor booleano, indicando se este controle será visível ou não quando o usuário já estiver logado na aplicação.

LoginView

Figura 4 – Controle LoginView.

Particularmente um dos controles mais interessantes. Ele exibe vários templates para diferentes situações (chamamos isso de ContentTemplate), dependendo de Roles de usuários e também tendo opção para definirmos o que vamos exibir quando o usuário ainda não está autenticado em nossa aplicação. Um cenário bastante utilizado, é quando temos áreas dentro da nossa aplicação, onde este conteúdo varia de acordo com o tipo de usuário, ou seja, quando o usuário é do tipo “Gerente”, exibe os valores das operações. Já quando o usuário é do tipo “Operador”, a quantidade de pendências é exibida.

Outro cenário muito comum, é quando o usuário ainda não encontra-se autenticado, e assim exibimos a seguinte mensagem: “Seja bem vindo ao nosso site.”. Quando o usuário se identifica, efetuando o Login, passamos a exibir uma mensagem mais customizada: “Olá José, seja bem vindo.”.

PasswordRecovery

Figura 5 – Controle PasswordRecovery.

Finalmente o controle PasswordRecovery. Este controle é responsável por enviar o password ao usuário. Mas isso terá um comportamento diferente, dependendo de algumas opções que definimos na configuração do provider. Se o atributo enablePasswordRetrieval estiver definido como True e o atributo passwordFormat não for Hashed, então o password do usuário é enviado em “clean text” para o usuário, caso contrário, é gerado automaticamente um novo password aleatório e posteriormente enviado ao respectivo usuário.

A classe Membership

Como já vimos anterioramente, está é uma classe abstrata, contendo uma série de métodos compartilhados. Esta classe recebe em runtime a instância da classe concreta, ou seja, do provider que definimos o arquivo Web.Config. Claro que estes métodos serão invocados da classe concreta, pois é onde se encontram os métodos implementados para uma Base de Dados específica.

Abaixo é exibido uma lista com os principais métodos desta classe:

Membro Descrição
 CreateUser Cria um novo usuário.
 DeleteUser Exclui um usuário.
 FindUsersByEmail Resgata os usuários baseando-se em um endereço de email.
 FindUsersByName Resgata os usuários baseando-se em um userName.
 GeneratePassword Gera um password aleatório.
 GetAllUsers Retorna todos os usuários.
 GetNumberOfUsersOnline Retorna um número inteiro representando todos os usuários online.
 GetUser Resgata um determinado usuário baseando-se em um userName.
 GetUsernameByEmail Resgata um determinado usuário baseando-se em um endereço de email.
 UpdateUser Atualiza as informações de um determinado usuário.
 ValidateUser Retorna um valor booleano indicando se o usuário existe ou não.

Para utilizar esta API, não há a necessidade que criar a sua instância, por motivos que já vimos acima. Basta apenas chamar os métodos compartilhados da classe Membership e já estará usufruindo de todo o Membership, e também, já refletindo as alterações na Base de Dados. Abaixo alguns exemplos:

1
2
3
4
5
 
‘Criando usuário
Membership.CreateUser(“IsraelAece”, “123456”)
 
‘Excluindo usuário
Membership.DeleteUser(“IsraelAece”)
 
Código 3 – Utilizando o Provider na aplicação.

CONCLUSÃO: Os Providers Models vieram em hora certa, onde agora, podemos trabalhar mais facilmente com código genérico, deixando assim a aplicação bastante flexível. Já que contamos com este design também na parte de segurança do ASP.NET, isso evitará de escrevermos muitos códigos, assim como fazíamos nas versões anteriores. Estas funcionalidades já encontram-se encapsuladas dentro dos Providers e controles disponibilizados para a nossa utilização.

Colorindo Colunas do DataGrid

Há momentos em que queremos deixar uma coluna em destaque do nosso DataGrid para chamar a atenção do Usuário ou mesmo para dar enfase em algo que seja extremamente importante.

Podemos fazer isso no DataGrid utilizando o evento ItemDataBound do mesmo, onde podemos colorir qualquer coluna dado um índice válido, que corresponde à coluna, iniciando por 0.

Abaixo o código necessário para isso:

1
2
3
4
5
6
7
8
9
 
Private Sub DataGrid1_ItemDataBound(ByVal sender As Object, ByVal e As _
    System.Web.UI.WebControls.DataGridItemEventArgs) Handles DataGrid1.ItemDataBound
 
    If e.Item.ItemType = ListItemType.AlternatingItem OrElse _
        e.Item.ItemType = ListItemType.Item Then
 
        e.Item.Cells(1).BackColor = System.Drawing.Color.LightSkyBlue
    End if
End Sub
 

Como podem ver, através da coleção de células (Cells) do nosso DataGrid, passamos um número inteiro que corresponde à coluna que queremos resgatar e assim, termos acesso as propriedades da mesma. Através da propriedade BackColor, definimos a nova cor que desejamos. Vale também chamar a atenção para a condicional que temos que fazer para termos a certeza que estamos manipulando apenas as “linhas” correspondentes a dados e descartando os Header e Footer do DataGrid.

Colorindo a Linha do DataGrid no Evento onMouseOver

Para que a navegação fique um pouco mais interativa para o Usuário, tornando-a bem mais inteligente.

Algo que podemos fazer para melhorar, é quando o Usuário passar o ponteiro do mouse pelas linhas do DataGrid, a cor do background da célula muda para que o mesmo tenha uma referência de onde esta.

Abaixo o código necessário para isso:

1
2
3
4
5
6
7
8
9
10
 
Private Sub DataGrid1_ItemDataBound(ByVal sender As Object, ByVal e As _
    System.Web.UI.WebControls.DataGridItemEventArgs) Handles DataGrid1.ItemDataBound
 
    If e.Item.ItemType = ListItemType.AlternatingItem OrElse _
        e.Item.ItemType = ListItemType.Item Then
 
        e.Item.Attributes.Add(“onMouseOver”, “this.style.backgroundColor=’#CCCCCC'”)
        e.Item.Attributes.Add(“onMouseOut”, “this.style.backgroundColor=’#FFFFFF'”)
    End if
End Sub
 

Através dos atributos do Item em questão, que neste caso é uma TR (TableRow), utilizamos os eventos onMouseOver e onMouseOut para definirmos as cores que desejamos que fique a nossa linha quando o cursor do mouse passar por cima dela e quando o cursor sair. Vale também chamar a atenção para a condicional que temos que fazer para termos a certeza que estamos manipulando apenas as “linhas” correspondentes a dados e descartando os Header e Footer do DataGrid.

Transformando XML com XSLT

Quando temos um arquivo XML, podemos querer exibí-lo para o Usuário. Como seu formato é incompreensível para Usuários que não conhecem, pode-se estruturá-lo para exibir de uma forma mais amigável.

Para isso utilizamos XSLT para transformar o XML, mostrando assim ao Usuário algo compreensível, utilizando HTML para formatar e exibir estes valores.

Temos abaixo os arquivos XML e XSLT:

Arquivo XML (Categorias.xml):

 

Arquivo XSLT (Formatacao.xslt):

Vemos que utilizamos um for-each para percorrer os dados do XML baseando-se nas tags: “Dados/Registro”.

Agora em nosso arquivo ASPX, temos que criar a estrutura para resgatar o conteudo do Arquivo XML e carregar o arquivo XSLT, e assim aplicar este estilo no conteúdo do arquivo XML, e gerando o seu “output” em HTML, para que o browser possa compreender.

Temos que importar os seguintes Namespaces:

1
2
3
4
 
Imports System.IO
Imports System.Xml
Imports System.Xml.Xsl
Imports System.Text
 

Agora faremos essa tranformação no evento Page_Load do nosso WebForm. O código abaixo mostra como efetuar:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
Private Sub Page_Load(ByVal sender As Object, ByVal e As _
    System.EventArgs) Handles MyBase.Load
 
    Dim arquivoXML As String = Server.MapPath(“Categorias.xml”)
    Dim arquivoXSLT As String = Server.MapPath(“Formatacao.xslt”)
    Dim xmlDOC As New XmlDocument
    Dim xsltDOC As New XslTransform
    Dim sb As New StringBuilder
    Dim sw As New StringWriter(sb)
 
    xmlDOC.Load(arquivoXML)
    xsltDOC.Load(arquivoXSLT)
    xsltDOC.Transform(xmlDOC, Nothing, sw, Nothing)
    Response.Write(sb.ToString)
End Sub
 

Criamos dois objetos, sendo um do tipo XmlDocument e o outro do tipo XslTransform e carregamos os respectivos arquivos em cada um desses objetos. Depois disso, chamamos o método “Transform”, que aplica o estilo do XSLT no XML e através de um objeto do tipo StringWriter recuperamos o “output” gerado e atribuímos à um StringBuilder, e este por sua vez armazena o conteúdo que por fim escrevemos na tela através do Response.Write.

Lendo XML de uma String

Em determinados momentos quando não temos um arquivo XML fisicamente criado, mas temos seu conteúdo, qual foi recuperado de algum método ou mesmo uma Query SQL em uma String e necessitamos carregar essa String (o conteúdo XML) em um DataSet, podemos utilizar a classe StringReader juntamente com o método ReadXml do Dataset para isso.

Esta classe está contida dentro do Namespace System.IO e implementa a classe TextReader para é utilizada para ler uma sequencia de caracteres.

Temos abaixo a estrutura do conteúdo XML que iremos carregar no nosso Dataset e posteriormente atribuí-lo à um DataGrid: 

Agora veremos o código necessário para ler a String e carregarmos no Dataset. Temos que importar o seguinte Namespace:

1
 
Imports System.IO
 

Dentro do evento Load do nosso WebForm fazermos:

1
2
3
4
5
6
7
8
9
10
11
12
13
 
Private Sub Page_Load(ByVal sender As Object, ByVal e As _
    System.EventArgs) Handles MyBase.Load
 
    Dim xml As String
    Dim ds As New Dataset
 
    xml = “<Pessoal><Funcionario><ID>00011</ID><Nome>Jose</Nome></Funcionario></Pessoal>”
 
    ds.ReadXml(New StringReader(xml), XmlReadMode.Auto)
 
    Me.DataGrid1.DataSource = ds
    Me.DataGrid1.DataBind()
End Sub
 

Chamo a atenção para o conteúdo atribuído à variável xml, que uma parte foi ocultada por questões de espaço. Através do método ReadXml do Dataset, passamos uma instância da classe StringReader que recebe no seu construtor a String XML que queremos ler. Informamos também no segundo parâmetro o XmlReadMode, que especifica como os dados em XML são lidos dentro do Dataset, e por fim, definimos a propriedade DataSource do DataGrid com o nosso Dataset já carregado e invocamos o método DataBind do DataGrid para carregá-lo.

DataGrid com Popup

Muitas vezes precisamos exibir um conjunto de dados em um controle do tipo DataGrid e como os dados não estão todos sendo exibidos ao Usuário podemos criar uma nova janela onde passamos como parâmetro para a mesma o ID, identificando o registro, e assim nesta nova janela efetuamos uma nova Query na DB e assim retornamos seu conteúdo na íntegra.

Através do Property Builder do DataGrid, podemos definir a nossa coluna que será o link que abrirá esta nova página para o Usuário como HyperLinkColumn, passando como parâmetro o ID do registro. Através da HyperLinkColumn podemos definir o texto que será o Link, que neste caso será o nome da Categoria vindo da DB, o campo da DB que indentifica o registro e o formato da URL.

Temos que definir todos esses campos para que seja enviado corretamente o valor a nova janela, principalmente na propriedade “URL Format String”, onde informamos a função Javascript que encontra-se no cliente (no arquivo ASPX).

Abaixo uma figura que nos mostra como definir os campos corretamente na HyperLinkColumn no Property Builder do DataGrid:

Depois destes dados definidos, resta criarmos a função “Visualizar” no arquivo ASPX, conforme é mostrado abaixo:

1
2
3
4
5
 
<script language=”javascript”>
    function Visualizar(id){
        window.open(‘Pagina.aspx?ID=’+ id, ”, ”);
    }
</script>
 

Infelizmente o único problema que temos quando definimos os parâmetros no Property Builder é que ele nos permite apenas utilizarmos um parâmetro dinâmico, e caso precise de mais de um, teríamos que interceptar o DataBind do DataGrid, e assim montar o link dinamicamente no código.