Error en deployment SAP Crystal Reports 2010

SAPLogo

Hola a todos!

Un post rapidito: Al intentar distribuir mediante ClickOnce una aplicación Winforms que utiliza Crystal Reports 2010 (si, algunos de nosotros todavía usamos Winforms y el viejo CR), es posible que durante la instalación os encontréis con este error:

Setup has detected that the file ‘C:\…\Crystal Reports for .NET Framework 4.0\CRRuntime_32bit_13_0.msi’ has changed since it was initially published. Click OK to retry the download, or Cancel to exit setup.

El problema es que uno de los ficheros (Product.xml) está firmado con una clave pública incorrecta. Para que no os tengáis que pelear con lo mismo que yo (aunque al fin y al cabo buceando un poco por los foros de SAP lo encuentras rápido), aquí está la solución:

  1. Descargar una nueva versión del fichero Products.xml desde esta ubicación (en realidad es un ZIP).
  2. Descomprimir el ZIP en cualquier carpeta.
  3. Copiar el fichero Product.xml a la siguiente ubicación:

C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bootstrapper\Packages\Crystal Reports for .NET Framework 4.0

(*) Si no estáis en un entorno x64 debéis utilizar ‘Program Files’ sin el x86.

Luego recompiláis la aplicación, publicáis otra vez, y listos!

Un saludo,

[Debate] Nombre de Namespaces en métodos extensores ¿Y tu que opinas?

Ayer, a raíz de un post del colega Javier Torrecilla sobre métodos extensores, unos cuantos de nosotros entre los que estaban el propio Javier y Jorge Serrano nos enzarzamos en una discusión en twitter acerca del mejor modo de declarar nuestros métodos extensores.

Pongamos un ejemplo: Supongamos que queremos crear un método extensor para comprobar si un valor está entre dos valores (el clásico between de toda la vida).

Agrego una clase llamada ExtensionMethods a mi proyecto, o a otro proyecto mi solución y agrego este código:

namespace CustomExtensions


{


    public static class ExtensionMethods


    {


        public static bool Between<T>(this T @value, T min, T max) where T : IComparable<T>


        {


            return @value.CompareTo(min) >= 0 && @value.CompareTo(max) <= 0;


        }


    }


}

Suponiendo que estamos en un proyecto de tipo WinForms, si queremos utilizar este método extensor sobre un valor de tipo int basta con ir a cualquier formulario y llamar al método between sobre un valor de este tipo. Por ejemplo:

EM1

Oops! Que pasa? Por que no aparece el método extensor? Bueno, como ya os habréis dado cuenta el método extensor está declarado dentro de un namespce llamado ‘CustomExtensions’, que es distinto al namespace del formulario en el que lo estoy probando, con lo que no podemos usarlo directamente si previamente no hacemos un using:

using CustomExtensions;

Vale, ahora si que aparece:

EM2

Bien. Esto en si no es nada del otro mundo, pero la cuestión es que si deseamos evitar declarar el using (tenéis que pensar que este método extensor lo podéis reutilizar en 1000 proyectos distintos), no tenemos otra opción que:

  1. Declarar el método extensor en un namespace que se llame igual que el namespace en el que se va a usar.
  2. Declarar el método extensor en un namespace que se llame igual que el namespace del tipo que estamos extendiendo.
  3. Pasar de todo y llamarlo al namespace como queramos, y que a la hora de usarlo debamos usar un using para agregarlo.

Particularmente soy partidario del segundo punto, de modo que si vamos a extender elementos de tipo IComparable, en lugar de usar el namespace ‘CustomExtensions’ prefiero usar el nombre del namespace que contiene la definición de este tipo, o sea ‘System’:

namespace System


{


    public static class ExtensionMethods


    {


        public static bool Between<T>(this T @value, T min, T max) where T : IComparable<T>


        {


            return @value.CompareTo(min) >= 0 && @value.CompareTo(max) <= 0;


        }


    }


}



Pero ese es mi punto de vista, tu que opinas? Twitteros manifestaos! :-)

HowTo: Crear una pantalla de inicio (splash screen)

Nota: Otro post en respuesta a una pregunta bastante habitual en los foros MSDN: ¿Cómo crear una pantalla de inicio para mi aplicación?

He creado un pequeño proyecto de ejemplo, que pueda servir como plantilla base para que cada uno se lo personalice para su aplicación. Este proyecto tiene lo básico: Un formulario sin bordes con una imagen, una barra de progreso, una etiqueta para el título, otra para ir mostrando mensajes, y un botón por si se desea cancelar la carga del programa (al estilo Office 2010).

SplashScreen

Él proyecto es muy sencillo y lo podéis descargar desde aquí:

La pantalla de inicio utiliza un thread para mostrar los diferentes mensajes al cargar, ya que así no se bloquea la aplicación (y la barra de progreso). Esto es así porque en el proyecto de ejemplo, al cargar la pantalla de inicio se lanza un segundo hilo que llama a un método ‘initApplication’, y desde éste método simulamos varios procesos largos (en realidad de un segundo cada uno), y cada vez que se inicia uno de ellos hay que cambiar el mensaje:

public void initApplication()


{


    Thread.Sleep(DEFAULT_TIME);


    this.Invoke((MethodInvoker)(() => setMessage("Searching for updates...")));


    Thread.Sleep(DEFAULT_TIME);


    this.Invoke((MethodInvoker)(() => setMessage("Connectiong to database...")));


    Thread.Sleep(DEFAULT_TIME);


    this.Invoke((MethodInvoker)(() => setMessage("Connectiong to webservices...")));


    Thread.Sleep(DEFAULT_TIME);


    this.Invoke((MethodInvoker)(() => setMessage("Loading settings...")));


    Thread.Sleep(DEFAULT_TIME);


    this.Invoke((MethodInvoker)(() => setMessage("Loading user preferences...")));


    Thread.Sleep(DEFAULT_TIME);


    this.Invoke((MethodInvoker)(() => setMessage("Starting application...")));


    Thread.Sleep(DEFAULT_TIME);


    if (this.InvokeRequired) this.Invoke(new Action(finishProcess));


}

Recordar que desde un hilo que no sea el hilo principal, en .NET no se puede actualizar la interfaz de usuario directamente. En su lugar debemos usar el método Invoke. En el ejemplo anterior llamamos a un método ‘setMessage’ que se encarga de mostrar el texto en la etiqueta correspondiente. Para poder llamar a este método mediante Invoke tenemos dos opciones: Podemos usar un MethodInvoker o un Action, en nuestro caso usaremos el primero, ya que un Action se usa cuando no hay paso de parámetros, y este método precisa de un parámetro con el mensaje a mostrar:

public void setMessage(string msg)


{


    messageLabel.Text = msg;


}

Una vez finalizado el proceso de carga, se cierra el formulario y se devuelve un DialogResult = Ok. Por otro lado si en cualquier momento de la carga el usuario ha pulsado el botón ‘close’, se hace lo mismo pero devolviendo un DialogResult = Cancel:

private void finishProcess()


{


    this.DialogResult = System.Windows.Forms.DialogResult.OK;


    this.Close();


}

void closeButton_Click(object sender, EventArgs e)


{


    this.DialogResult = System.Windows.Forms.DialogResult.Cancel;


    this.Close();


}

Como veis el proyecto es muy sencillo, sólo debéis recordar un detalle importante: En una aplicación WinForms el punto de entrada a la misma se define en el método estático Main del Program.cs, y aquí hay una línea que inicializa el formulario inicial de nuestra aplicación:

Application.Run(new fMain());

Lo primero que solemos pensar es que aquí deberíamos lanzar el formulario fSplashScreen, y al cerrarlo mostrar el formulario principal, verdad? Pues no, no podemos hacer eso. El motivo no es otro que este formulario inicial va a definir el ciclo de vida de nuestra aplicación, y si lo cerramos, cerramos la aplicación. Ya se que en VB puede cambiarse este comportamiento, pero entre nosotros… hacerlo siempre me ha parecido una chapuza :-)

Así pues, aquí lanzaremos el formulario principal, y éste, al cargarse (mientras todavía no es visible) lanzará la pantalla de inicio de forma modal y esperará el valor de retorno. Si al cerrarse la pantalla de bienvenida el valor de retorno es Ok, continúa la carga y muestra el formulario principal, en caso contrario cierra el formulario principal y con por ende la aplicación:

void fMain_Load(object sender, EventArgs e)


{


    showSplashScreen();


}


 


private void showSplashScreen()


{


    using (fSplashScreen fsplash = new fSplashScreen())


    {


        if (fsplash.ShowDialog() == System.Windows.Forms.DialogResult.Cancel) this.Close();


    }


}



Espero que sirva como ejemplo a todos aquellos que desean tener una pantalla de inicio para sus aplicaciones. Un saludo!



Andorra, Noviembre 2010

HowTo: Crear un hook de teclado para registrar una hotkey en nuestra aplicación

hook

Hola de nuevo,

En los grupos de MSDN suele haber bastantes preguntas acerca de crear hooks de teclado, para que nuestra aplicación pueda ejecutar alguna acción determinada, en respuesta a alguna pulsación de teclado, aunque no esté activa.

He creado una pequeña clase llamada WindowsShell que contiene el código necesario para registrar una hotkey y asociarla a un formulario:

   1: using System;


   2: using System.Runtime.InteropServices;


   3: using System.Windows.Forms;


   4:  


   5: namespace TestRegisterHotKey


   6: {


   7:     public class WindowsShell


   8:     {


   9:         public enum ModifierEnum


  10:         {


  11:             MOD_ALT = 0x1,


  12:             MOD_CONTROL = 0x2,


  13:             MOD_SHIFT = 0x4,


  14:             MOD_WIN = 0x8


  15:         }


  16:  


  17:         private static int keyId;


  18:         public static int WM_HOTKEY = 0x312;


  19:  


  20:         [DllImport("user32.dll")]


  21:         private static extern bool RegisterHotKey(


  22:             IntPtr hWnd, int id, int fsModifiers, int vlc);


  23:  


  24:         [DllImport("user32.dll")]


  25:         private static extern bool UnregisterHotKey(


  26:             IntPtr hWnd, int id);


  27:  


  28:         public static void RegisterHotKey(Form f, Keys key)


  29:         {


  30:             int modifiers = 0;


  31:             if ((key & Keys.Alt) == Keys.Alt)


  32:                 modifiers = modifiers | (int)WindowsShell.ModifierEnum.MOD_ALT;


  33:             if ((key & Keys.Control) == Keys.Control)


  34:                 modifiers = modifiers | (int)WindowsShell.ModifierEnum.MOD_CONTROL;


  35:             if ((key & Keys.Shift) == Keys.Shift)


  36:                 modifiers = modifiers | (int)WindowsShell.ModifierEnum.MOD_SHIFT;


  37:             Keys k = key & ~Keys.Control & ~Keys.Shift & ~Keys.Alt;


  38:             f.Invoke(new Action(() =>


  39:             {


  40:                 keyId = f.GetHashCode();


  41:                 RegisterHotKey((IntPtr)f.Handle, keyId, modifiers, (int)k);


  42:             }));


  43:         }


  44:  


  45:         public static void UnregisterHotKey(Form f)


  46:         {


  47:             try


  48:             {


  49:                 f.Invoke(new Action(() =>


  50:                 {


  51:                     UnregisterHotKey(f.Handle, keyId);


  52:                 }));


  53:             }


  54:             catch (Exception)


  55:             {


  56:                 throw;


  57:             }


  58:         }


  59:  


  60:     }


  61: }

Posteriormente, para poder usar esta clase en un formulario cualquiera debemos interceptar el bucle de mensajes de Windows, y preguntar si se ha producido el mensaje en cuestión, y si la pulsación corresponde a nuestra hotkey:

   1: using System;


   2: using System.Windows.Forms;


   3:  


   4: namespace TestRegisterHotKey


   5: {


   6:     public partial class Form1 : Form


   7:     {        


   8:         public Form1()


   9:         {


  10:             InitializeComponent();


  11:             this.Shown += Form1_Shown;


  12:             this.FormClosed += Form1_FormClosed;


  13:         }


  14:  


  15:         void Form1_FormClosed(object sender, FormClosedEventArgs e)


  16:         {


  17:             WindowsShell.UnregisterHotKey(this);


  18:         }


  19:  


  20:         void Form1_Shown(object sender, EventArgs e)


  21:         {


  22:             Keys k = Keys.A | Keys.Control;


  23:             WindowsShell.RegisterHotKey(this, k);


  24:         }


  25:  


  26:         protected override void WndProc(ref Message m)


  27:         {


  28:             if (m.Msg == WindowsShell.WM_HOTKEY)


  29:             {


  30:                 Keys key = (Keys)(((int)m.LParam >> 16) & 0xFFFF);


  31:                 WindowsShell.ModifierEnum modifier = 


  32:                     (WindowsShell.ModifierEnum)((int)m.LParam & 0xFFFF);


  33:                 if (key == Keys.A && modifier == WindowsShell.ModifierEnum.MOD_CONTROL)


  34:                     showForm();


  35:             }


  36:             base.WndProc(ref m);


  37:         }


  38:  


  39:         private void showForm()


  40:         {


  41:             this.Text = 


  42:                 String.Format("Hokey pressed at: {0}", 


  43:                 DateTime.Now.ToLongTimeString());


  44:             this.Show();


  45:             if (this.WindowState == FormWindowState.Minimized)


  46:                 this.WindowState = FormWindowState.Normal;


  47:         }


  48:     }


  49: }



 



Espero que a más de uno le sirva,



HYEI :-)



MVP por octavo año! Gracias a todos :-)

En mitad de la vorágine del TTT 2010 (ya sabéis, un evento de geeks, nerds y frikis pero que en realidad es una tapadera, y todo se reduce a cerveza, sexo y Rock & Roll) tuve lo que los alcohólicos llaman un momento de lucidez. De modo que apuré el vaso, despedí a las chicas y a la banda de rock, y me puse a consultar el correo de un modo febril, a ver si encontraba un mensaje en especial… Y es que cada 1 de Octubre de los últimos 8 años he recibido un mail que no estoy muy seguro de merecer, pero por si acaso… ¡yo no digo nada! :-D


mvplogohor


Se trata de la notificación de bienvenida al programa MVP de Microsoft:


Enhorabuena. Nos complace presentarle el programa de nombramiento MVP de Microsoft® de 2010. Este nombramiento se concede a los líderes excepcionales de la comunidad técnica que comparten de forma activa su experiencia de alta calidad y de la vida real con otras personas…


El hecho de recibir esta notificación me produce una tremenda alegría, no sólo por el reconocimiento que conlleva (y que como os decía antes, no estoy muy seguro de merecer), sino porque me permite estar en contacto con un grupo de una tremenda calidad, tanto técnica como humana. Sin duda alguna, esto es lo mejor del programa MVP.


Aquí tenéis algunos momentos de estos años:


DCP_1608DCP_1652DCP_1629


P1010037P1010059P1010029


fb1fb2fb3


 


fb6fb4fb5


 


Son simplemente algunos momentos escogidos al azar entre tantos buenos momentos. Encontraréis a algunos de los que ya no están, y también veréis a algunos que casi no se reconocen, jejeje… A ver que día me pongo a revisar más a fondo los cientos de archivos de fotos que tengo (sobretodo de los primeros años), seguro que aparecen auténticas ‘perlas’:-)


Un abrazo enorme a todos, los que son MVP, los que no, y en general a todos aquellos que dedican su tiempo a hacer más grande la comunidad. Todos son MVP, tengan o no tengan la distinción. Chavales, entre todos estamos contribuyendo a hacer algo grande.


Nos vemos en las comunidades y eventos por esos mundos de Yupi! :-P


Lluís Franco, Octubre 2010


** crossposting desde el blog de Lluís Franco en geeks.ms **

Materiales de la charla: Parallel computing @ BcnDev

13Antes de nada: Lo se, soy un desastre. Hace ya más dos semanas del evento que realizamos en BcnDev, y debería haberlos subido hace unos cuantos días… pero he estado unos días de vacaciones (totalmente offline), y la vuelta ha sido demasiado traumática para pensar en cosas como esta :-)


Es lo que suele suceder cuando no te acercas a menos de 10 metros de un ordenador durante más de dos semanas (por orden judicial). Luego a la vuelta todo resulta un poco más difícil y acabas preguntándote si realmente quieres hacer esto el resto de tu vida :-P


En fin, os dejo por un lado la PPT de la charla y por otro el proyecto Web que ejecutaba las demos desde un WebRole en AZURE. Lógicamente el proyecto ya no está publicado en AZURE, más que nada porque estaba configurado para usar 8 cores y eso cuesta una pasta gansa al mes. Sin embargo, lo podeis instalar en vuestro entorno de desarrollo VS2010 para “cacharrear” un poco. Eso si, quiero recordaros que para poder experimentar paralelismo real deberá existir más de un core, y las máquinas virtuales acostumbran a tener sólo uno.


 



PPT:



http://cid-f3a970280830b5fe.office.live.com/view.aspx/AndorraDotNet/Events/BcnDev-Parallel/News40-ParallelComputing.pptx


Código fuente proyecto web:



http://cid-f3a970280830b5fe.office.live.com/self.aspx/AndorraDotNet/Events/BcnDev-Parallel/DemoParallelWeb.zip


Es bastante posible que el evento se repita en el mes de Octubre, así que manteneros a la escucha en Facebook o Twitter.


Nos vemos!


** crossposting desde el blog de Lluís Franco en geeks.ms **

Evento BcnDev Reloaded – Jueves 22 de Julio

El próximo jueves tenemos otro evento en Barcelona. Espero veros allí :-)

 

jueves, 22 de julio

AGENDA             

09:00 – Registro
09:30 –  Introducción.
10:00 – Novedades Visual Studio 2010 y .net 4.0
11:00 – Parallel Computing with .NET 4.0.
12:00 – Entity Framework 4.0.
13:00 – Novedades ASP.NET 4.0 y JQuery con Visual Studio 2010.
14:00 – Sorteo de licencias, productos y otras sorpresas.

Ponentes

Para este evento podremos disfrutar de ponentes de amplia experiencia.

  • Benjami Adell
  • Luis Franco
  • Pep Lluis Baño
  • Marc Rubiño

Registro

https://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032455485&Culture=es-ES


** crossposting desde el blog de Lluís Franco en geeks.ms **

Creando aplicaciones para Windows Phone 7 usando Silverlight

Ya empiezan a verse los primeros ejemplos interesantes de desarrollo para Windows Phone 7.


En el blog de ScottGu, podéis encontrar dos pequeños ejemplos que utilizó en su charla del MIX10 (vídeo aquí). El primero es un sencillo Hello world, y el segundo utiliza LINQ to XML y el APi de Twitter para mostrar todas los tweets de un tag determinado (en el ejemplo scottgu).


Darle un vistazo porque vale la pena, y además, más sencillo imposible! :-)


Artículo original en:


http://weblogs.asp.net/scottgu/archive/2010/03/18/building-a-windows-phone-7-twitter-application-using-silverlight.aspx


image


Saludos! :-)


** crossposting desde el blog de Lluís Franco en geeks.ms **

Silverlight 4.0 RC disponible

Silverlight340x98


Al fin! Después de unas semanas de incertidumbre, por fin se ha desvelado que Silverlight 4.0 verá la luz el próximo mes. Suerte de aquellos que puedan asistir al ASP.NET and Silverlight Conference en las Vegas, del 12 al 14 de Abril, porque además podrán ver el lanzamiento de VS2010 a nivel mundial.


De momento y para hacer boca (y poder desarrollar con la Release Candidate sobre VS2010 :-P), os dejo el enlace y algo e información (en inglés) para que os podáis descargar las herramientas necesarias:


  • Silverlight 4 Tools for Visual Studio 2010
    This will install the developer runtime of Silverlight 4, the Visual Studio project support and the Silverlight 4 SDK. If you are developing Silverlight 4 applications, this will be the minimum you want to install!
  • Windows Runtime or Mac Runtime
    If you installed the tools above, you will get the developer runtime and there is no need to install again. These downloads are being made available for test machines for the Windows and Mac platforms for your applications.
  • Expression Blend 4 Beta
    This is a beta version of Expression Blend that will enable authoring of Silverlight 4 applications.
  • Silverlight Toolkit
    The toolkit has been updated to provide support for Visual Studio 2010 and Silverlight 4 development. The Toolkit provides numerous controls for your application and source code is also included for these controls using an Open Source license.
  • WCF RIA Services
    Microsoft WCF RIA Services simplifies the traditional n-tier application pattern by bringing together the ASP.NET and Silverlight platforms. The RIA Services provides a pattern to write application logic that runs on the mid-tier and controls access to data for queries, changes and custom operations. It also provides end-to-end support for common tasks such as data validation, authentication and roles by integrating with Silverlight components on the client and ASP.NET on the mid-tier.

Más información en:


http://silverlight.net/getstarted/silverlight-4/


Saludos,


** crossposting desde el blog de Lluís Franco en geeks.ms **

Usando ASP.NET membrership en Winforms (3 / n)

El modelo de objetos de membresía (Membership object model)


Membershipom


O lo que es lo mismo, el pan nuestro de cada día en lo relativo a administrar la seguridad basada en membresía. Para acceder a este cojunto de clases es necesario agregar los namespaces System.Web y System.Web.Extensions, que contienen las clases necesarias para interactuar con la base de datos de membresía descrita en el artículo anterior.


References


Conocer este conjunto de clases es vital para poder aprovechar todas las características de este sistema, de modo que aquí va una tabla con las principales elementos y funcionalidades:


Clase/interfaz

Funciones

Membership

Proporciona los servicios de suscripción generales.

Crea un nuevo usuario.

Elimina un usuario.

Actualiza un usuario con nueva información.

Devuelve una lista de usuarios.

Encuentra un usuario por el nombre o el correo electrónico.

Valida (autentica) un usuario.

Obtiene el número de usuarios conectados.

Busca los usuarios por el nombre de usuario o la dirección de correo electrónico.

MembershipUser

Proporciona información sobre un usuario concreto.

Obtiene la contraseña y la pregunta de la contraseña.

Cambia la contraseña.

Determina si el usuario está conectado.

Determina si el usuario está validado.

Devuelve la fecha de la última actividad, del último inicio de sesión y del último cambio de contraseña.

Desbloquea un usuario.

MembershipProvider

Define la funcionalidad de los proveedores de datos que el sistema de suscripción puede utilizar.

Define los métodos y las propiedades que necesita implementar un proveedor utilizado en la suscripción.

MembershipProviderCollection

Devuelve una colección de todos los proveedores disponibles.

MembershipUserCollection

Almacena las referencias a los objetos MembershipUser.

MembershipCreateStatus

Proporciona los valores descriptivos de éxito o error al crear un nuevo usuario suscrito.

MembershipCreateUserException

Define la excepción que se produce si no se puede crear un usuario. Hay un valor de enumeración MembershipCreateStatus disponible a través de la propiedad StatusCode que describe el motivo de la excepción.

MembershipPasswordFormat

Especifica los posibles formatos de almacenamiento de contraseñas utilizados por los proveedores de suscripciones incluidos con ASP.NET (Clear, Hashed, Encrypted).


Configurando nuestra aplicación


Nota: Esto pensaba dejarlo para más adelante, pero lo posteo aquí también por si alguien quiere animarse a hacer pruebas ;-)


Para poder acceder al modelo de objetos, además de agregar las dos referencias que hemos comentado antes, también es necesario modificar el fichero de configuración de la aplicación (Web.config en un proyecto Web y App.config en un proyecto WinForms). Existen multitud de artículos que describen cómo hacerlo en el primero de los casos, así que nos vamos a centrar en el segundo.


Empezaremos con la configuración mínima para que funcione nuestra aplicación, y posteriormente iremos viendo distintas opciones para permitir que el sistema de seguridad se comporte de un modo distinto, por ejemplo, pidiendo una contraseña no tan restrictiva, o que no sea necesario indicar el email del usuario, o la pregunta y respuesta de seguridad.


El primer paso para configurar nuestra aplicación es agregar un fichero de configuración (App.config) a nuestro proyeco, o si ya tenemos uno, abrirlo para su edición. Toda la configuración se va a centrar en dos secciones: <connectionStrings> y <system.web>. En la primera es especificará una entrada que apunte a la base de datos de membresía, mientras que en la segunda se especificarán los valores para los proveedores de usuarios y roles:


<configuration>
  <connectionStrings>
    <add name="MemberShipConnectionString"
         connectionString="data source=.\SQLEXPRESS;Integrated Security=True;DataBase=Test"
         providerName="System.Data.SqlClient"/>
  </connectionStrings>
  <system.web>
    <roleManager enabled="true">
      <providers>
        <clear/>
        <add name="AspNetSqlRoleProvider"
             type="System.Web.Security.SqlRoleProvider"
             connectionStringName="MemberShipConnectionString"
             applicationName="Test"/>
      </providers>
    </roleManager>
    <membership defaultProvider="SqlProvider">
      <providers>
        <clear/>
        <add name="SqlProvider" 
             type="System.Web.Security.SqlMembershipProvider"
             connectionStringName="MemberShipConnectionString"
             applicationName="Test"/>
      </providers>
    </membership>
  </system.web>
</configuration>

Oservemos que la cadena de configuración apunta a la BD que creamos en el post anterior, y que esta cadena de conexión se usa en la configuración de los proveedores de usuarios y roles. Otro detalle muy importante es que la clave applicationName contiene el nombre de nuestra aplicación para el sistema de membresía, de modo que todos los usuarios y roles que se creen estarán agrupados bajo este nombre de aplicación.


Por defecto, esta configuración hace que al momento de crear un usuario sea necesario proporcionar: Nombre, contraseña de un mínimo de 7 carácteres y uno de ellos ‘especial’ (por ejemplo: @#$%), email, pregunta de seguridad, respuesta de seguridad, y si va a estar aprobado o no. No está nada mal, de hecho podríamos decir que la configuración predeterminada es a su vez, la más segura.


Si deseamos cambiar esta configuración, par permitir que un usuario no pueda preguntarle al sistema cuál es su contraseña (sólo pueda resetearla), o por ejemplo para ‘relajar’ un poco la seguridad (que quede claro que esto no lo recomiendo en absoluto, y que es simplemente un ejercicio didáctico) podemos agregar atributos al fichero de configuración:


<membership defaultProvider="SqlProvider">
  <providers>
    <clear/>
    <add name="SqlProvider" 
         type="System.Web.Security.SqlMembershipProvider"
         connectionStringName="MemberShipConnectionString"
         applicationName="Test"
         enablePasswordRetrieval="false" 
         enablePasswordReset="true" 
         requiresQuestionAndAnswer="false" 
         requiresUniqueEmail="false" 
         passwordFormat="Hashed"/>
  </providers>
</membership>

Tenéis una lista completa de los atributos en las propiedades de la clase SqlMemberShipProvider.


Y ahora, un poco de ‘chicha’ en forma de código:


Ejemplos a cholón:


Crear un usuario:


MembershipUser user = Membership.CreateUser("admin", "$admin123");

Más sencillo imposible, verdad? Ahora bien, hay que tener presente que este código fallará con la configuración predeterminada, ya que debe suministrarse email, y la pregunta y respuesta de seguridad. En su lugar debería usarse este código:


MembershipCreateStatus status;
MembershipUser user = Membership.CreateUser(
    "admin", "$admin123", "admin@test.com",
    "pregunta de seguridad?", "respuesta de seguridad", 
    true, out status);
if (status == MembershipCreateStatus.Success)
{
    MessageBox.Show("Usuario creado correctamente!");
}

Bueno, tampoco no es demasiado complicado. La única diferencia es que se proporcionan más argumentos y que devuelve un objeto que define el estado de la creación del usuario, para saber si se ha creado con éxito.


Eliminar un usuario:


Membership.DeleteUser("admin");

Casi demasiado facil :-P


Obtener un usuario:


MembershipUser user = Membership.GetUser("admin");

A partir de este usuario recuperado podemos efectuar cualquier operación soportada.


Obtener todos los usuarios:


MembershipUserCollection users = Membership.GetAllUsers();
foreach (MembershipUser user in users)
{
    Console.WriteLine(user.UserName);
}

Obtenemos una colecció e iteramos por ella para mostrar los nombres.


Validar un usuario:


if (Membership.ValidateUser("admin", "$admin123"))
{
    MessageBox.Show("usuario correcto!");
}
else
{
    MessageBox.Show("usuario o contraseña incorrecto!");
}

Ideal para formularios de inicio de sesión. Tened cidado, que si se introduce mal 3 veces se bloquea el usuario.


Obtiene el número de usuarios conectados:


int num = Membership.GetNumberOfUsersOnline();

Sin comentarios.


Busca usuarios por el nombre de usuario o la dirección de correo electrónico:


MembershipUserCollection users = Membership.FindUsersByName("pepe");
MembershipUserCollection users = Membership.FindUsersByEmail("pepe@test.com");

Del mismo modo que GetAllUsers devuelve una colección de usuarios.


Obtiene la contraseña de un usuario:


MembershipUser user = Membership.GetUser("admin");
string pwd = user.GetPassword();
string pwd = user.GetPassword("respuesta de seguridad");
Tiene dos sobrecargas, en la segunda debe especificarse la respuesta de seguridad.

Sólo es válido si se ha especificado el atributo EnablePasswordRetrieval.


Cambia la contraseña de un usuario:


MembershipUser user = Membership.GetUser("admin");
user.ChangePassword("$admin123", "@nuevapassword2010");

Resetea la contraseña de un usuario:


MembershipUser user = Membership.GetUser("admin");
string pwd = user.ResetPassword();
string pwd = user.ResetPassword("respuesta de seguridad");

Tiene dos sobrecargas, en la segunda debe especificarse la respuesta de seguridad.


Sólo es válido si se ha especificado el atributo EnablePasswordReset.


Desbloquea un usuario:


MembershipUser user = Membership.GetUser("admin");
bool success = user.UnlockUser();

Particularmente útil cuando tenemos un usuario bloqueado, ya sea por un intento de intrusión, como por la típica amnesia matutina.


Y esto es todo por hoy, la semana que viene más :-)


Espero que esto anime a más de uno a usar este sistema de gestión de usuarios, ya veis que no es nada complicado.


Saludos a 0º (hoy ni frío ni calor),


Nota: Es la primera vez que escribo en el blog todos los días de una semana, así que esto hay que celebrarlo con un cafelito. Hasta luego! :-)




Artículos anteriores de la serie:


Usando ASP.NET membrership en Winforms (1 / n)


Usando ASP.NET membrership en Winforms (2 / n)


** crossposting desde el blog de Lluís Franco en geeks.ms **