Desenvolvimento - ASP. NET

Utilizando Web Parts com Oracle no ASP.NET

Veremos como criar um provider para utilizar os controles de Web Part do ASP.NET gravando o estado das páginas no Oracle.

por Eduardo Bottcher



Um dos recursos que popularizou o Sharepoint foi a utilização de web parts, módulo ou funcionalidade que pode ser facilmente adicionado ou removido de uma página pelo próprio usuário. A versão 2.0 do framework .NET adicionou ao ASP.NET a possibilidade de utilizar esse conceito de web parts através de novos controles como WebPartManager, WebPartZone, WebControls.WebPart, etc.

Basicamente uma web part é adicionada a uma página aspx da mesma forma como se adiciona um user control. Essa web part não pode ficar "flutando" na página mas deve estar localizada dentro de alguma WebPartZone. Um das caracteristicas desse framework de web parts é que o estado das páginas que contém as web parts é guardado no banco de dados para cada usuário. Isso quer dizer que quando um usuário adiciona uma web part à uma webpartzone de uma página, essa configuração é guardada em um banco de dados para que da próxima vez que ele abra a página, ele possa ver as web parts e as configurações que ele tinha previamente. Por padrão essa base de dados deve ser uma base SQL Server. O próprio ASP.NET cria esse banco, a estrutura de tabelas e deixa a coisa bem transparente e fácil para o programador, com o custo, claro, de deixar o ambiente menos flexível. Pensando nisso, a Microsoft implementou o conceito de Provider para separar a lógica de funcionamento das web parts na página das operações que vão ao banco gravar os dados. Com isso é possivel que nós criemos nosso próprio Provider para customizar essa operações de acordo com o nosso ambiente sem ter que mexer na parcela do código que não tem relação com as nossas regras de negócio.

Neste artigo iremos desenvolver um OraclePersonalization Provider, que vai possibilitar a criaçao de páginas de web parts gravando os seus dados em uma base Oracle.

Criando a tabela no Oracle

O primeiro passo é adicionar a tabela que vai armazenar as configurações das web parts no Oracle. Abra a sua IDE de preferência do Oracle e cria a tabela segundo o script abaixo:
CREATE TABLE personalization (
       CONSTRAINT un_pers UNIQUE (username, application, pagefile),
       username        varchar2(255),
       pagefile        varchar2(255),
       application     varchar2(1000),
       pagesettings    blob default empty_blob()
    )
O campo usename vai conter o nome do usuário , o campo application conterá o nome da aplicação e o campo pagefile, o nome do arquivo aspx que contém as configurações, que são guardadas no campo pagesettings em formato binário (BLOB). Os campos username, application e pagefile formam uma constraint para que um mesmo usuário em uma mesma página de uma mesma aplicação nao tenha duas configurações diferentes.

Estrutura básica da classe OraclePersonalization

Vamos criar uma classe que herde da classe UI.WebControls.WebParts. PersonalizationProvider e vamos implementar alguns métodos que sobrescrevemos da classe mãe. Clique com o botão direto no projeto de web da sua solução (Web application ou Web Site) e selecione Add" Class. Nomeie o arquivo para OraclePersonalizationProvider.cs

Substitua o código da classe criada pelo código escrito abaixo:
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.Hosting;
using System.Collections.Specialized;
using System.Text;

using Oracle.DataAccess.Client;
using Oracle.DataAccess.Types;

namespace TestOracleProvider
{
    public class OraclePersonalizationProvider: PersonalizationProvider
    {
        private string _applicationName = "MinhaAplicacao";

        private string connString ="Connection String para a base Oracle que voce criou a tabela";

        public override string ApplicationName
        {
            get
            {
                return _applicationName;
            }
            set 
            {
                _applicationName = value;
            }
        }

        public override void Initialize(string name, NameValueCollection configSettings)
        {
            base.Initialize(name, configSettings);
        }

        public override PersonalizationStateInfoCollection FindState(PersonalizationScope 
scope,PersonalizationStateQuery query, int pageIndex, int pageSize,out int totalRecords)
        {
            throw new NotImplementedException("FindState method was not 
implemented in OraclePersonalizationProvider Class");
        }
        
        public override int GetCountOfState(PersonalizationScope scope,PersonalizationStateQuery query)
        {
            throw new NotImplementedException("GetCountOfState method was not implemented in 
OraclePersonalizationProvider Class");
        }

        public override int ResetUserState(string path,DateTime userInactiveSinceDate)
        {
            throw new NotImplementedException("ResetUserState method was not implemented in 
OraclePersonalizationProvider Class");
        }

        protected override void ResetPersonalizationBlob(WebPartManager webPartManager,
string path, string userName)
        {
            throw new NotImplementedException("ResetPersonalizationBlob method was not implemented in 
OraclePersonalizationProvider Class");
        }

        public override int ResetState(PersonalizationScope scope,string[] paths, string[] usernames)
        {
            throw new NotImplementedException("ResetState method was not implemented in 
OraclePersonalizationProvider Class");

        }

        protected override void SavePersonalizationBlob(WebPartManager webPartManager,string path,string 
userName,byte[] dataBlob)
        {
            throw new NotImplementedException("SavePersonalizationBlob method was not implemented in 
OraclePersonalizationProvider Class");
        }

        protected override void LoadPersonalizationBlobs(WebPartManager webPartManager, string path, string 
userName,ref byte[] sharedDataBlob,ref byte[] userDataBlob)
        {
            throw new NotImplementedException("LoadPersonalizationBlobs method was not implemented in 
OraclePersonalizationProvider Class");

        }
      
    }
}

Esta é estrutura básica do nosso Personalization Provider. Em princípio, todos os métodos estão lançando uma exceção para que identifiquemos caso algum deles seja chamado durante a execução do código. Repare que logo no inicio do código que voce deve alterar o namespace para o namespace da aplicação em que voce está criando o Provider. Mude também o valor da connection string para apontar para a sua base Oracle e também o valor da variável _applicationName para o nome que deseja identificar melhor sua aplicação.

Nota: Repare que há duas cláusulas "using" no código referenciando a biblioteca Oracle.DataAccess. Isso porque estamos fazendo o acesso ao banco de dados diretamente no nosso provider. Em uma estrutura de camadas, haverá uma camada de dados que irá centralizar esse acesso.

Para o funcionamento básico do nosso provider precisamos implementar apenas três métodos: SavePersonalizationBlob, LoadPersonalizationBlobs e ResetPersonalizationBlob.

Implementando o método SavePersonalizationBlob

Este é o método que o framework de web parts chama toda vez que uma alteração nas configurações da página ocorre. Essa mudança pode ser a inclusão ou exclusão de uma nova web part ou a mudança de alguma web part de webpartzone. Substitua o código atual do método pelo código descrito abaixo:
protected override void SavePersonalizationBlob(WebPartManager webPartManager,
string path,string userName,byte[] dataBlob)
        {
            OracleConnection con = new OracleConnection(connString);
            con.Open();
            StringBuilder sql = new StringBuilder();
            string pagefile = HttpContext.Current.Request.ServerVariables["script_name"].ToString();

            sql.Append("BEGIN ");
            sql.Append("  DELETE FROM personalization WHERE username = :U1");
            sql.Append("    AND application = :A1 ");
			sql.Append("    AND pagefile = :P1; ");
            sql.Append("  INSERT INTO personalization ");
            sql.Append("    VALUES (:U2, :P2, :A2, :BLOBDATA); ");
            sql.Append("END;");
            
            if (string.IsNullOrEmpty(userName))
            {
                sql = sql.Replace("= :U1", "IS NULL");
                sql = sql.Replace(":U2", "NULL");
            }

            OracleCommand cmd = new OracleCommand(sql.ToString(),con);
            cmd.BindByName = true;
            cmd.CommandType = CommandType.Text;

            cmd.Parameters.Add(new OracleParameter("A1", OracleDbType.Varchar2,
                1000, this.ApplicationName, ParameterDirection.Input));
            
            cmd.Parameters.Add(new OracleParameter("A2", OracleDbType.Varchar2,
                1000, this.ApplicationName, ParameterDirection.Input));

			cmd.Parameters.Add(new OracleParameter("P1", OracleDbType.Varchar2,
                255, pagefile, ParameterDirection.Input));
            
            cmd.Parameters.Add(new OracleParameter("P2", OracleDbType.Varchar2,
                255, pagefile, ParameterDirection.Input));
            
            OracleParameter p = new OracleParameter("BLOBDATA", OracleDbType.Blob);
            p.Value = dataBlob;
            cmd.Parameters.Add(p);
            if (userName.Length > 0)
            {
                cmd.Parameters.Add(new OracleParameter("U1", OracleDbType.Varchar2,
                     255, userName, ParameterDirection.Input));
                cmd.Parameters.Add(new OracleParameter("U2", OracleDbType.Varchar2,
                     255, userName, ParameterDirection.Input));
            }
            cmd.ExecuteNonQuery();
            cmd.Dispose();
            con.Close();
            con.Dispose();
        } 

Basicamente este código recebe todos os parámetros necessários como username e blob, adiciona aos valores presentes no provider como application name e pagefile , e realiza a operação de gravar os dados no banco.

Implementando o método LoadPersonalizationBlobs

Este é o método que o framework de web parts chama toda vez que uma página de web parts é carregada para recuperar as configurações salvas previamente. Substitua o código atual do método pelo código descrito abaixo:
protected override void LoadPersonalizationBlobs(WebPartManager webPartManager, 
string path, string userName,ref byte[] sharedDataBlob,ref byte[] userDataBlob)
        {
            OracleConnection con = new OracleConnection(connString);
            con.Open();
            string pagefile = HttpContext.Current.Request.ServerVariables["script_name"].ToString();

            string sql = "SELECT pagesettings FROM personalization ";
            
            if (userName.Length > 0)
                sql += "WHERE username = :U1 and application = :A1 AND pagefile = :P1";
            else
                sql += "WHERE username IS NULL and application = :A1 AND pagefile = :P1";
            
            OracleCommand cmd = new OracleCommand(sql,con);

            cmd.BindByName = true;
            cmd.CommandType = CommandType.Text;
            
            if (userName.Length > 0)
            {
                cmd.Parameters.Add(new OracleParameter("U1", OracleDbType.Varchar2,
                     255, userName, ParameterDirection.Input));
            }
            
            cmd.Parameters.Add(new OracleParameter("A1", OracleDbType.Varchar2,
                1000, this.ApplicationName, ParameterDirection.Input));

			cmd.Parameters.Add(new OracleParameter("P1", OracleDbType.Varchar2,
                255, pagefile, ParameterDirection.Input));

           
            OracleDataReader reader = cmd.ExecuteReader();
           
            if (reader.Read())
            {
                if (reader["pagesettings"].ToString() != string.Empty)
                {
                    long blob_length = reader.GetBytes(0, 0, null, 0, Int32.MaxValue);
                    if (blob_length > 0)
                    {
                        byte[] buffer = new byte[blob_length];
                        reader.GetBytes(0, 0, buffer, 0, buffer.Length);
                        if (string.IsNullOrEmpty(userName))
                            sharedDataBlob = buffer;
                        else
                            userDataBlob = buffer;
                    }
                }
            }
            cmd.Dispose();
            con.Close();
            con.Dispose();
        }

Repare que tanto no método de salvar quanto no método de carregar os dados, existem algumas linhas de código que se preocupam em saber se o username é nulo ou não. Isto acontece porque é possivel criar uma página de web parts comum para todos os usuários. Para estes casos, chamado de visão compartilhada da página, o asp.net informa o username como nulo. Portanto, no método LoadPersonalizationBlobs, se o usuário for nulo a variável sharedDataBlob é populada com os dados do banco, do contrário quem recebe o valor é a variável userDataBlob.

Implementando o método ResetPersonalizationBlob

O ultimo método necessário para fazer nosso provider funcionar é o método ResetPersonalizationBlob. Este método é chamado quando o usuário exclui a última web part de uma determinada página. Neste caso ao invés de chamar o método SavePersonalizationBlob, o framework chama este método. Porém, o que precisamos fazer aqui é apenas chamar o método de save para que ele salve o valor nulo no campo pagesettings do banco. Substitua o código atual do método pelo código descrito abaixo:
 protected override void ResetPersonalizationBlob (WebPartManager webPartManager,
 string path, string userName)
 {
   SavePersonalizationBlob(webPartManager, path, userName, null);
 }

Adicionando o provider no Web.Config

Por último, vamos dizer à aplicação para usar o novo provider. Adicione esta entrada no web config debaixo de <system.web>:
<webParts>
   <personalization defaultProvider="OraclePersonalizationProvider">
   <providers>
      <add name="OraclePersonalizationProvider" 
	  type="TestOracleProvider.OraclePersonalizationProvider"/>
   </providers>
   </personalization>
</webParts>

Agora você já pode criar as suas páginas com web parts, que elas já estarão sendo gravadas no Oracle. Com esse provider voce pode ter os benefícios da web part com maior controle do ambiente. Assim como fizemos esse provider para o Oracle, essa técnica pode ser aplicadao para qualquer banco de dados. Mãos à obra!

Have Fun!
Eduardo Bottcher

Eduardo Bottcher - Microsoft MCP em ASP.NET/C#, Eduardo Bottcher trabalha como arquiteto de software na HNI Corporation em Iowa, Estados Unidos. É pós graduado em desenvolvimento web e trabalha com tecnologias Microsoft há mais de 7 anos. Atualmente tem contado suas aventuras e desventuras na américa no blog http://bottech.blogspot.com. Seu blog sobre tecnologia é http://onbottcher.spaces.live.com.