domingo, 28 de octubre de 2007

(SQLTableProfileProvider) Uso de un proveedor de perfiles personalizado

Todos sabemos lo fácil de implementar que son los controles en ASP.Net, y en parte es eso lo que mas nos atrae(hasta cierto punto), con la promesa de comenzar a producir de manera casi inmediata, con una mínima inversión de tiempo en aprendizaje; desgraciadamente, no siempre se puede adecuar a las necesidades particulares de todos.

Es así como es muy común verse en la necesidad de "ampliarlos" o "extenderlos" para que se ajusten a nuestras necesidades, y sobre eso voy a hablar en las próximas lineas.

Como base en la creacion de sitios u aplicaciones web, es indispensable(generalmente) mantener un registro de usuarios y limitar su acceso al sitio en relación a sus credenciales, y en esto ASP.net nos facilita la vida mediante el uso de roles y membresías.

Por otra parte, muchas veces nos encontramos con la necesidad de almacenar datos de usuarios diferentes a aquellos que vienen por defecto en el control CreateUserWizard de ASP.net. La solución? extenderlo para que se adecue a nuestras necesidades.

Antes de embarcarnos en como alterar nuestra configuración para poder almacenar datos adicionales a los más comunes y ya incluidos por defecto(nombre,pass,mail,pregunta y respuesta secreta) es imperativo que veamos que tipo de tratamiento vamos a necesitar aplicar sobre ellos.

El proveedor de perfiles que ASP.net posee por defecto es fácilmente ampliable, solo con un par de líneas de código en el web.config y un par más en el code behind tenemos un proveedor de perfiles que puede almacenar toda la información que queramos. El problema con esto es la manera en la que se almacena dicha información en la base de datos, y si luego necesitamos hacer una consulta a la tabla aspnet_profiles nos daremos contra una verdadera pared.

El proveedor guarda la información de perfil de la siguiente manera:

Field Name Values
PropertyNames FirstName:S:0:6:LastName:S:7:6:
PropertyValuesString Andrew Rimmer
PropertyValuesBool Null

Donde, PropertyNames son los nombres de las "propiedades" o información extra que estamos agregando, PropertyValuesString almacena los valores de los strings y PropertyValuesBool los valores booleanos de la propiedad.


En PropertyNames, tengo el valor del primer campo "FirstName", seguido por el tipo "S" de String(Andrew es un string), cuyo valor inicia en la posición 0 y va hasta la 6(Andrew tiene 6 caracteres), seguido por LastName que se analiza de manera análoga. En la columna PropertyvaluesString se almacena el valor propiamente dicho de los strings (Andrew para FirstName), y en la PropertyValuesBool se almacena el valor de las propiedades booleanas si las tuviera. Si esto en un embrollo con solo 2 campos adicionales agregados, imagínense lo que sería si necesitamos guardar 10.

Por lo anterior, o hacemos un complejo tratamiento de strings para obtener la información, o implementamos nuestro propio proveedor de perfiles.

A continuación voy a mostrarles como hacer ambas cosas, por un lado voy a extender el proveedor de perfiles que ya viene con ASP.net, y luego voy a mostrarles como implementar uno propio, que almacena los valores de las propiedades en una tabla personalizada que nosotros mismos creamos y cuya disposición determinamos nosotros.

Extensión del Proveedor de datos por defecto de ASP.net:

Primero modificamos nuestro web.config de la siguiente manera:

Dentro de la declaración de profile, y luego de el cierre del tag providers, agregamos properties y procedemos a añadir allí cada uno de los campos de información extra que necesitamos de acuerdo a nuestras necesidades:
<profile defaultProvider="SqlProvider">
<providers>
<clear />
<add name="SqlProvider" type="System.Web.Profile.SqlProfileProvider"
connectionStringName="SqlServices"
applicationName="MiAplicacion"
description="SqlProfileProvider"/>
</providers>
<properties>
<add name="Firstname" type="string"/>
<add name="Lastname" type="string"/>
</properties>
</profile>

Una vez hecho esto agregamos un poco de codigo en el Code behind:
protected void CreateUserWizard1_CreatedUser(object sender, EventArgs e)
{
ProfileCommon p = (ProfileCommon)ProfileCommon.Create(CreateUserWizard1.UserName, true);
p.Firstname = ((TextBox)CreateUserWizard1.CreateUserStep.ContentTemplateContainer.FindControl("Firstname")).Text;
p.Lastname = ((TextBox)CreateUserWizard1.CreateUserStep.ContentTemplateContainer.FindControl("Lastname")).Text;
p.Save();
}

Dentro del evento created User del CreateUserWizard creamos un profileCommon p, el cual ya tendrá las propiedades agregadas al webconfig, por lo que lo único que debemos hacer es localizar aquellos controles que agregamos a nuestro CreateUserWizard (en este caso textboxes) y asignar los valores correspondientes. Luego de esto doy la indicación de que el perfil se salve, y voila tengo almacenada información extra sobre mi usuario.

Implementación de un proveedor de perfiles personalizado:

Existe un trabajo por parte de un mienbro del ASP.net team de microsoft que nos permite implementar un proveedor personalizado de perfiles (AQUI podrán descargar los archivos necesarios así como los papers para ver como instalarlo).

Este hombrecito nos provee de 2 proveedores el SQLTableProfileProvider y el SQLStoreProcedureProfileProvider. Para nuestros fines utilizaremos el primero (sientanse libres de explorar sobre el segundo) que básicamente me permite almacenar los datos del perfil en una tabla creada personalmente, con la disposición de campos que desee, con la única condición necesaria de incluir UserID y LastUpdatedDate, necesarios para la gestión interna de ASP.net.

OK, lo primero que hacemos es crear la tabla en la base de datos, la cual podemos llamar como queramos, en nuestro ejemplo se llamará "Perfil", recuerden colocar los permisos de la tabla de manera correcta

grant SELECT,INSERT,UPDATE,DELETE on dbo.Perfil to [SuNombreDeMaquina\ASPNET]

go

Como el SqlTableProfileProvider accede a la tabla directamente, deben asegurarse de permitir los derechos de SELECT, INSERT, UPDATE y DELETE a la cuenta correspondiente del web server. En el ejemplo se usa la cuenta ASPNET puesto que es ésta la cuenta de procesos ASP.net en una PC con Windows XP. Con seguridad integrada se utiliza el proceso identity para acceder a la Base de Datos. Si estan usando Windows Server 2003 e IIS6 entonces NETWORK SERVICE es la cuenta a utilizar. Finalmente si solo usan seguridad standar SQL, deben conceder los derechos a la cuenta de usuario SQL correspondiente.

La última parte de configuración a nivel de Base de datos es la de conceder derechos a algunos procedimientos almacenados de ASP.net que son automticamente instalados en su BD cuando se instalan los esquemas para Membresias, Roles, etc.

grant EXECUTE on dbo.aspnet_Applications_CreateApplication to [YOURMACHINENAME\ASPNET]

grant EXECUTE on dbo.aspnet_Users_CreateUser to [YOURMACHINENAME\ASPNET]

grant SELECT on dbo.aspnet_Users to [YOURMACHINENAME\ASPNET]

grant UPDATE on dbo.aspnet_Users(LastActivityDate) to [YOURMACHINENAME\ASPNET]

go

De igual manera a lo anterior tambien deberan conceder los derechos de los procedimientos almacenados a la cuenta correspondiente.

Una vez hecho lo anterior procedemos a modificar nuestro web.config de la siguiente manera:

Es imperativo tener la connectionstring en el web.config para que esto funcione.

cada linea agregada al web.config es "self explanatory":

<profile defaultProvider="TableProfileProvider">
<providers>
<clear />
<add name="TableProfileProvider"
type="Microsoft.Samples.SqlTableProfileProvider"
connectionStringName="ConnectionString"
table="Perfil"
applicationName="MiAplicacion"/>
</providers>
<properties>
<add name="Firstname" defaultValue="[null]"
customProviderData="Firstname;varchar"
provider="TableProfileProvider"/>
<add name="Lastname" defaultValue="[null]"
customProviderData="Lastname;varchar"
provider="TableProfileProvider"/>
</properties>
</profile>

/clear para indicar que no quiero usar el proveedor por defecto(almacenado en el machine.config) y que voy a definir el propio.

add para indicar que voy a agregar un proveedor, name y type son entendibles, luego el nombre de el connectionstring.

Y aqui la informacion clave: table="Perfil" aquí coloco el nombre de la tabla en la que deseo guardar la informacion adicional, applicationName="NombreDeMiAplicacion"(indispensable haberlo definido previamente, para mayores referencias lean mi post anterior).

Una vez definido todo esto, lo único que me resta es definir las properties de una manera similar a la que especifique anteriormente, observen que no solo coloco el nombre de la propiedad, sino tambien el campo de la Base de Datos a la que este se va a bindear.

Otra cosa que deberan hacer es bajarse el instalador del proveedor y compilar los SqlStoredProcedureProfileProvider.cs y SqlTableProfileProvider.cs que se encuentran en (por defecto) C:\Archivos de programa\Microsoft\ASP.NET 2.0 Table Profile Provider Samples\App_Code\, utilizen el Command Prompt del vs para hacerlo; una vez hecho esto(ver AVISO abajo) agregamos la dll al directorio bin/ de nuestra aplicación, y estamos listos para usarlo.

AVISO: Hay un pequeño inconveniente a solucionar cuando quieran hacer la compilación, en el caso del SQLStoreProcedure se va a compilar perfectamente, pero el SQLTable no, les dará un error como

"SqlTableProfileProvider.cs(75,36): error CS0103: The name 'SqlStoredProcedureProfileProvider' does not exist in the current context
SqlTableProfileProvider.cs(82,28): error CS0103: The name 'SqlStoredProcedureProfileProvider' does not exist in the current context"

Para solucionar esto lo que YO hice es agregar el codigo del SQLStoredProcedureProfileProvider AL INICIO del SQLTableProfileProvider, guardarlo, y luego compilarlo.

Realmente el uso de esta última opción nos presenta innumerables ventajas a la hora de intentar "editar" usuarios mediante nuestras aplicaciones en ASP.net, hasta ahora es la única forma en la que he logrado implementar la opción de "editar perfiles" desde dentro de la aplicación web propiamente dicha(desconozco si existe alguna manera más "facil", estuve buscando por un tiempo y no pude encontrar nada).

Como siempre el feedback está mas que agradecido, si descubren algun error(soy humano y los cometo) sientase libres de comunicármelo, cualquier crítica(siempre constructiva) será mas que bien recibida por mi parte.

Bueno, eso es todo por ahora, espero que les sirva de algo(a mi mismo me hizo recapacitar sobre ciertas cosillas que debo reveer y comunicar a mi Jefe :P).

Saludos y Happy Programming my little fellows.


Leandro

No hay comentarios: