No meu artigo O Utilitário Aspnet_regsql e os Schemas do SQL Server vimos como instalar a base de dados dos Providers ASP.NET em um Schema diferente do dbo. Porém, isso por si só não é suficiente uma vez que os Providers continuarão a procurar pelos objetos no dbo. A solução para resolver a questão do aceso, é personalizar os Providers de forma que procurem pelos objetos da base no Schema correto.
O Web Part Personalization Provider
O ASP.NET utiliza uma série de Providers para se comunicar com a base de dados de suporte e atender a serviços como Membership e Web Parts. Esses Providers possuem uma estrutura padrão de execução baseada no arquivo Web.config para localizar a base de dados de suporte. Embora a utilização do Web.config signifique que alguns elementos usados pelos Providers possam ser parametrizados, isso não é verdade para qualquer informação. Por exemplo, dois elementos importantes que não podem ser parametrizados sem personalização são justamente o nome do Schema associado a base de dados e a possibilidade de usarmos conexões dinâmicas. No exemplo abaixo vemos uma típica seção de configuração para utilização dos Web Parts em uma aplicação. Note que não há um parâmetro que indique o nome do Schema na base de dados:
<webParts>
<personalization defaultProvider=“AspNetSqlPersonalizationProvider“>
<providers>
<add name=“AspNetSqlPersonalizationProvider“> type=“System.Web.UI.WebControls.WebParts.SqlPersonalizationProvider“ connectionStringName=“LocalSqlServer“ applicationName=“/“ />
</providers
<authorization>
<deny users=“*“ verbs=“enterSharedScope“ />
<allow users=“*“ verbs=“modifyState“ />
</authorization
</personalization
</webParts
Agora vejamos a mesma seção de configuração utilizando o Provider personalizado, contendo o nome do Schema em uso e sem uma String de conexão fixa:
<webParts>
<personalization defaultProvider=“AppWebPartProvider“>
<providers>
<clear />
<add name=“SitePersonalizationProvider“> type=“SiteApp.SitePersonalizationProvider“ databaseSchema=“Configuracoes“ applicationName=“SiteApp“ />
</providers
<authorization>
<deny users=“*“ verbs=“enterSharedScope“ />
<allow users=“*“ verbs=“modifyState“ />
</authorization
</personalization
</webParts
Para demonstrar como chegar a essa personalização, falarei especificamente sobre o SqlPersonalizationProvider, embora os princípios sirvam para os demais Providers. O primeiro passo na personalização dos Providers ASP.NET é baixar o ProviderToolkitSamples aqui, que contém um projeto C# (aparentemente, não há uma versão em VB.NET do Toolkit) com exemplos de personalização desses Providers. Instale o Toolkit e em seguida abra o projeto ProviderToolkitSampleProviders.csproj. No conjunto de arquivos há o fonte de cada Provider e rotinas de apoio. Para trabalharmos as Web Parts especificamente precisaremos apenas dos arquivos:
- SqlPersonalizationProvider.cs
- PersonalizationProviderHelper.cs
- SecUtil.cs
- SqlConnectionHelper.cs
- SR.cs
Você pode criar um novo projeto C# e adicionar apenas os arquivos citados, caso queira manter o projeto original intacto. Nossa primeira alteração será no arquivo SqlPersonalizationProvider.cs. Vamos declarar uma nova variável no escopo da classe chamada _databaseSchemaName, que terá o objetivo de armazenar o nome correto do Schema da base de dados:
private const int maxStringLength = 256;
private string _applicationName;
private int _commandTimeout;
private string _connectionString;
private int _SchemaVersionCheck;
private string _databaseSchemaName;
Em seguida, localize no código todas as chamadas ao Schema dbo. Em cada linha encontrada substituiremos dbo pela variável que vai conter o nome correto do Schema, por exemplo:
String strSchemaCommand = _databaseSchemaName + “.aspnet_PersonalizationAdministration_FindState“;
SqlCommand command = new SqlCommand(strSchemaCommand, connection);
O objetivo da nova variável é trazer o nome correto do Schema, que informaremos no arquivo Web.config da aplicação através de um novo parâmetro chamado databaseSchema. Nos Providers, o método Initialize é responsável por ler as configurações do Web.config logo, é nesse método que vamos verificar a presença do atributo contendo o nome do Schema que vamos utilizar. Acrescente a seguinte verificação ao método Initialize:
//Se o schema do banco não for informado, assume dbo como padrão.
//O atributo databaseSchema é personalizado, não existindo no elemento de configuração padrão.
if (String.IsNullOrEmpty(configSettings[“databaseSchema“]))
{
configSettings.Add(“databaseSchema“,“dbo“);
}
Em seguida, acrescente ao final do método Initialize a linha de código que recupera o nome do Schema no arquivo de configuração:
_databaseSchemaName = configSettings[“databaseSchema“];
Por fim, altere o método CheckSchemaVersion passando um parâmetro adicional, que criaremos na sequência:
private void CheckSchemaVersion( SqlConnection connection )
{
string[] features = { “Personalization“ };
string version = “1“;
SecUtility.CheckSchemaVersion( this,
connection,
features,
version,
_databaseSchemaName,
ref _SchemaVersionCheck );
}
Quando todas as chamadas aos objetos do banco estiverem recuperando o Schema correto, teremos agora que alterar o arquivo SecUtil.cs. Nesse arquivo, localize o método CheckSchemaVersion e altere a sua assinatura para conter um novo parâmetro de tipo String chamado databaseSchema. A nova assinatura deve estar como abaixo:
internal static void CheckSchemaVersion(ProviderBase provider, SqlConnection connection, string[] features, string version, string databaseSchema, ref int schemaVersionCheck)
Em seguida na mesma rotina, localize a chamada pela procedure aspnet_CheckSchemaVersion e altere o código para que fique como abaixo:
String strSchemaCommand = databaseSchema + “.aspnet_CheckSchemaVersion“;
cmd = new SqlCommand(strSchemaCommand, connection);
As alterações feitas até o momento permitem determinarmos um Schema diferente do dbo. Para permitir o uso de uma conexão informada dinâmicamente vamos criar uma propriedade no arquivo SqlPersonalizationProvider.cs como abaixo:
//Propriedade para manter a string de conexão
public string ConnectionString
{
get { return _connectionString; }
set { _connectionString = value; }
}
E voltando ao método Initialize vamos verificar se a String de conexão foi informada pelo Web.config ou dinâmicamente:
//Verifica se a conexão foi informada no arquivo de configuração. Se não foi, deve ser informada através da propriedade ConnectionString.
string connectionStringName = configSettings[“connectionStringName“];
if (!String.IsNullOrEmpty(connectionStringName))
{
configSettings.Remove(“connectionStringName“);
string connectionString = SqlConnectionHelper.GetConnectionString(connectionStringName, true, true);
if (String.IsNullOrEmpty(connectionString))
{
throw new ProviderException(SR.GetString(SR.PersonalizationProvider_BadConnection, connectionStringName));
}
_connectionString = connectionString;
}
A Chamada em uma Aplicação Web VB.Net
O que fizemos até o momento foi criar um Provider personalizado. O passo seguinte é chamar esse Provider na aplicação Web e claro, o VB.Net não pode faltar 🙂 Para isso crie um novo projeto Web e adicione uma classe com o nome que você queira dar ao seu Provider de acesso. Nessa classe coloque o seguinte código:
Public Class SitePersonalizationProvider
Inherits AppWebPartProvider
Private strApplicationName As String = “SiteApp“
Public Overrides Sub Initialize(name As String, config As System.Collections.Specialized.NameValueCollection)
‘Verifica se a seção de configuração dos Web Parts existe no arquivo de configuração
If config Is Nothing Then
Throw New ArgumentNullException(“config“, “A configuração dos Web Parts não está definida no arquivo de configurações“)
End If
‘Se o nome do Provider não estiver configurado, associamos o nome padrão
If String.IsNullOrEmpty(name) Then
name = “SitePersonalizationProvider“
End If
‘Atribui a descrição do Provider se não houver um definido no arquivo de configuração
If String.IsNullOrEmpty(config(“description“)) Then
config.Remove(“description“)
config.Add(“description“, “Provider de personalização de Web Parts“)
End If
MyBase.Initialize(name, config)
Dim strCn As String = “A string de conexão entra aqui“
MyBase.ConnectionString = strCn
End Sub
End Class
Vimos então um exemplo de personalização em um dos Providers ASP.NET para acessar de maneira diferente a base de dados, mas muito mais pode ser feito dependendo dos requisitos da aplicação. Espero que o artigo seja útil na necessidade de customizações com os Providers do ASP.NET.