[WebCast] Aprovecha el poder de facebook en tu web.

Hola a todos! Este post es sólo para informaros que el próximo 4 de febrero a las 19:00, voy a realizar un webcast para la gente del Club .NET UOC. Bajo el título Aprovecha el poder de facebook en tu web, voy a contar algunas cosillas sobre como integrar facebook en tu aplicación ASP.NET: usar connect para implementar un SSO (single sign-on), poner mensajes en el muro, obtener información del perfil del usuario…

La idea es ver lo fácil que resulta la integración y las ventajas que podemos obtener al integrarnos con una red social en general, y en una de las más usadas en particular…

Os dejo el enlace para apuntaros. Cualquier cosa que queráis comentar… ya sabéis donde encontrarme!!! 😉

Saludos!

PD: Evidentemente, muchas gracias a la gente del Club .NET UOC y a Jesús Bosch en particular, por invitarme a realizar el webcast… es un autentico placer!

RA000 (o MSB3217): Cannot Register assembly Foo.dll (Method does not have an implementation).

Hola! Un post cortito, sobre un error que me he encotrado… Al compilar un proyecto, marcado para interoperabilidad COM VS.NET se me ha quejado con el siguiente error:

c:WINDOWSMicrosoft.NETFrameworkv3.5Microsoft.Common.targets(3019,9): error MSB3217: Cannot register assembly "C:TeamserverPhoenixRefactoringCoreDevelopmentCore-WI5825-SIO4binDebugPhoenixContainer.dll". Method ‘GetDefaultIWorkspace’ in type ‘CaixaPenedes.Phoenix.Core.CompositeUI.ShellUserControl’ from assembly ‘Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=77c76132715b70fa’ does not have an implementation.

Yendo al directorio bin/Debug y ejecutar regasm PhoenixContainer.dll daba el mismo error.

La situación era la siguiente:

  1. Tengo dos soluciones distintas, una de las cuales compila (entre otros) el ensamblado Core.dll, donde está la clase ShellUserControl que supuestamente tiene el método no implementado. Por supuesto el método está implementado. La otra solución compila PhoenixContainer.dll, el ensamblado que me da el error.
  2. PhoenixContainer.dll tiene una referencia (entre otros a Core.dll).
  3. Ambas soluciones compilan contra el mismo directorio de salida.

Buscando por ahí, he visto que más gente tenía el mismo error… en esta página dan con la que parece ser la causa principal: que al ejecutar regasm, los ensamblados que regasm usen no sean los mismos contra los que se ha compilado el proyecto. Generalmente eso puede ser debido a versiones incorrectas en la GAC.

Este no era mi caso: Yo tenia Core.dll y PhoenixContainer.dll en el mismo directorio y no uso la GAC, así que es imposible que me estuviese pillando alguna otra Core.dll.

Finalmente he decidido poner en marcha fuslogvw y que me mostrase todos los "bind failures”, es decir todas las veces que el CLR intenta cargar un ensamblado, y por la razón que sea no lo encuentra. Y touché: Ha aparecido un bind failure: Regasm.exe intentaba cargar Microsoft.Practices.Composite.UI.dll (este ensamblado forma parte de CAB). El ensamblado Core.dll tiene una referencia contra Microsoft.Practices.Composite.UI.dll, pero con copy local a false, puesto que usamos un directorio “compartido” donde hay varios ensamblados externos a nuestro proyecto. Sospecho que la razón por la cual Regasm.exe intenta cargar Microsoft.Practices.Composite.UI.dll (y no otros ensamblados también referenciados por Core.dll) es porque el método GetDefaultIWorkspace es público y devuelve un IWorkspace, tipo definido en este ensamblado. Lo curioso, es que este método nunca es utilizado desde PhoenixContainer.dll (aunque el tipo ShellUserControl sí).

Así en el bin/debug tenía Core.dll pero no Microsoft.Practices.Composite.UI.dll y esa era la causa del error: modificando la referencia con copy local a true, todo ha funcionado correctamente!

Cada vez tengo más claro que el copy local a false, sólo trae que compilaciones, a excepción que los archivos referenciados estén en la GAC…

Saludos!

Objetos que notifican sus cambios de propiedades (2/3): Publish and subscribe

Nota: Este post es el segundo post de la serie Objetos que notifican sus cambios de propiedades.

En el post anterior vimos como configurar Unity para que no tener que añadir código adicional para implementar la interfaz INotifyPropertyChanged. En este post quiero hablaros de un patrón que se utiliza mucho cuando hablamos de aplicaciones complejas: el patrón del publicador – suscriptor. En este patrón tenemos básicamente dos conceptos:

  1. El publicador: Cuando un objeto quiere notificar algo al respecto de su estado, se limita a publicar un mensaje con la información deseada.
  2. El suscriptor: Los subscriptores reciben todos aquellos mensajes a los que están suscritos, con independencia de quien los haya publicado.

Este patrón se diferencia del modelo de eventos estándard de .NET, en que para realizar una suscripción a un tipo de mensaje no es necesario tener referencia alguna a quien pueda publicar este mensaje. En el sistema de eventos no és así: si quiero recibir información sobre el click de un botón, debo tener una referencia a este botón, para poder registrar la función gestora del evento:

button1.Click += new EventHandler(button1_Click);

Este modelo de eventos directos tiene sus limitaciones y da en Winforms bastantes quebraderos de cabeza (especialmente cuando tenemos un formulario con un usercontrol formado por varios usercontrols que a su vez están formados por más usercontrols y queremos propagar un evento del usercontrol  más interno al formulario). Es cierto que WPF introduce dos mejoras interesantes como los routed events (que ayudan precisamente a solventar este problema de usercontrols anidados) y los commands, pero ninguno de ambos mecanismos ofrece la misma flexibilidad que el modelo de publicación – suscripción.

Créeme: si desarrollas una aplicación compleja, ya sea en winforms o en WPF, te beneficiará mucho el uso de un modelo de publicación – suscripción (no en vano tanto CAB+SCSF como PRISM incorporan uno).

Vamos a ver como podemos implementarnos uno que, aunque sencillito, sea lo suficientemente funcional…

1. El notificador de mensajes

Lo primero que debemos crear es el notificador de mensajes, es decir el objeto que usamos para publicar un mensaje y el que usamos también para informar a que tipo de mensajes queremos suscribirnos.

El notificador de mensajes va a tener esta interfaz:

public interface ICommandNotifier
{
/// <summary>
/// Devuelve la lista de los commands actuales. Los commands se
/// añaden automáticamente cuando se realiza un publish de cualquier
/// tipo nuevo.
/// </summary>
IEnumerable<Type> Commands { get; }
/// <summary>
/// Añade una suscripción al tipo de command TPayload
/// </summary>
/// <typeparam name="TPayload">Tipo de command al que nos suscribimos</typeparam>
/// <param name="func">Acción a ejecutar cuando se publique el command</param>
/// <param name="filterFunc">Método que se evalúa sobre el payload para determinar
/// si el command se pasa o no al suscriptor.</param>
/// <returns>Token de suscripción</returns>
SubscriptionToken Subscribe<TPayload>(Action<TPayload> func, Func<TPayload, bool> filterFunc);

/// <summary>
/// Publica un command. El tipo de command es el tipo de la clase del payload.
/// </summary>
/// <param name="payload">Payload (datos) del commanad</param>
void Publish(object payload);

Básicamente sólo tiene un método para suscribirse a un determinado tipo de mensajes y otro método para publicarlos. Una implementación más compleja nos permitiría también eliminar suscripciones (es decir cuando ya no me interesa seguir recibiendo notificaciones de determinados commands)… pero eso lo dejamos como ejercicio 🙂

La implementación tampoco es excesivamente compleja (no pongo el código aquí, ya que lo tenéis en el zip que adjunto al final del post). Básicamente lo que hace es:

  1. Mantiene una lista de todos los tipos de mensajes que se hayan lanzado. Lo que determina si un mensaje es de un tipo u otro es su clase (en la implementación una lista de objetos CommandInfo).
  2. Por cada mensaje de esa lista mantiene una lista con todos los suscriptores (en la implementación objetos de la clase AllTimeSubscriber).
  3. Por cada suscriptor de cada mensaje mantiene básicamente dos delegates:
    1. El delegate que sirve para decidir si se envía este mensaje a este suscriptor (parámetro filterFunc del método Subscribe)
    2. El delegate que debe invocarse en el suscriptor (parámetro func del método Subscribe).

Sólo un apunte: el notificador de mensajes vamos a registrarlo en Unity como un singleton, eso significa que existirá sólo uno y que estará vivo durante toda la ejecución del programa. Por lo tanto, si guardamos directamente los delegates en el notificador de mensajes, impedirá al garbage collector actuar sobre los suscriptores (recordad que un delegate mantiene una referencia a un objeto en concreto y a un método). Para solucionar esto me he creado una clase, que he llamado WeakDelegate, que tiene la misma información que un delegate, pero usa una WeakReference para apuntar al objeto (el suscriptor) y de esta manera permitir actuar al garbage collector. Recordad: siempre que guardeis referencias en un singleton considerad el uso de WeakReference!

2. Cambiar la implementación de nuestro handler

Una vez tenemos un notificador de mensajes, sólo debemos cambiar la implementación de nuestro ICallHandler (clase AutoPropertyChangedHandler) de Unity, para usar dicho notificador. Para ello en el método Invoke en lugar de llamar al método RaiseEvent (para lanzar el evento PropertyChanged) como hacíamos en el post anterior, vamos a usar el notificador para publicar un mensaje de tipo PropertyChangedCommand:

// Si el setter no produce excepción, publicamos un command de tipo PropertyChangedCommand
if (raiseEvt && msg.Exception == null)
{
cmdNotifier.Publish(new PropertyChangedCommand(propName, input.Target));
}

La clase PropertyChangedCommand es una clase que nos hemos creado nosotros que no hace nada más que guardar el nombre de la propiedad que ha cambiado y el objeto sobre el cual ha cambiado la propiedad.

Como recibe la clase AutoPropertyChangedHandler el notificador de mensajes? Pues se le pasa en el constructor:

public AutoPropertyChangedHandler(ICommandNotifier cmdNotifier)
{
this.cmdNotifier = cmdNotifier;
}

Ahora sólo debemos modificar la clase AutoPropertyChangedAttribute para que cuando cree el objeto AutoPropertyChangedHandler  le pase el notificador de mensajes. La forma más fácil es aprovechar que en AutoPropertyChangedAttribute tenemos acceso a Unity, para devolver el objeto AutoPropertyChangedHandler usando Resolve y que de esa manera Unity inyecte el notificador de mensajes:

public override ICallHandler CreateHandler(IUnityContainer container)
{
return container.Resolve<AutoPropertyChangedHandler>();
}

3. El suscriptor

Finalmente nos queda crear el suscriptor. Los suscriptores son clases normales que usan el notificador de mensajes para suscribirse a tipos de mensajes. P.ej. el siguiente suscriptor se suscribe a los mensajes cuyo tipo sea PropertyChangedCommand:

public Suscriptor(ICommandNotifier cmdNotif)
{
cmdNotif.Subscribe<PropertyChangedCommand>(this.DoPropertyChangedCommand, this.CanDoPropertyChangedCommand);
}

private void DoPropertyChangedCommand(PropertyChangedCommand payload)
{
Console.WriteLine("Propiedad {0} modificada", payload.PropertyName);
}

private bool CanDoPropertyChangedCommand(PropertyChangedCommand payload)
{
bool retVal = !payload.PropertyName.Equals("Name");
Console.WriteLine("CanDoPropertyChanged con prop {0} devuelve {1}", payload.PropertyName, retVal);
return retVal;
}

Fíajos en la función CanDoPropertyChangedCommand: esta función se evalúa cada vez que alguien publica un command y sólo en el caso que devuelva true se ejecutará la función DoPropertyChangedCommand que es la que “procesa” el mensaje. En este caso, este suscriptor está interesado en recibir todos los cambios de calquier propiedad excepto “Name”.

Finalmente sólo nos queda crear un suscriptor y probar el código. En el método Main() tengo:

container.RegisterType<ICommandNotifier, CommandNotifier>(new ContainerControlledLifetimeManager());
A2 a2 = container.Resolve<A2>();
Suscriptor subs = container.Resolve<Suscriptor>();
a2.Name = "edu";
a2.Edad = 10;
// Registramos el notificador como singleton
container.RegisterType<ICommandNotifier, CommandNotifier>(new ContainerControlledLifetimeManager());
// Creamos un A2...
A2 a2 = container.Resolve<A2>();
// ... y un suscriptor
Suscriptor subs = container.Resolve<Suscriptor>();
a2.Name = "edu";
a2.Edad = 10;

Y listos! Sí lo ejecutais veréis que la salida es:

CanDoPropertyChanged con prop Name devuelve False
CanDoPropertyChanged con prop Edad devuelve True
Propiedad Edad modificada

Ya tenemos implementado nuestro propio publicador-suscriptor!

Os dejo un zip con todo el código (en skydrive).

Un saludo!

Objetos que notifican sus cambios de propiedades (1/3): La intercepción

Nota: Este post es el primer post de la serie Objetos que notifican sus cambios de propiedades.

En este post vamos a ver como configurar la intercepción de Unity, para poder inyectar nuestro código cada vez que se modifiquen las propiedades de un objeto.

Los que desarrolléis en WPF sabréis que existe una interfaz llamada INotifyPropertyChanged, que se puede implementar para notificar a la interfaz de usuario de que las propiedades de un objeto (generalmente ligado a la interfaz) han modificado, y que por lo tanto la interfaz debe actualizar sus datos.

Esta interfaz define un solo evento, llamado PropertyChanged que debe lanzarse para informar del cambio de propiedad. Es responsabilidad de cada clase lanzar el evento cuando sea oportuno:

public class A : INotifyPropertyChanged
{
private string _name;

// Evento definido por la interfaz
public event PropertyChangedEventHandler PropertyChanged;

// Lanza el evento "PropertyChanged"
private void NotifyPropertyChanged(string info)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
// Propiedad que informa de sus cambios
public string Name
{
get { return _name; }
set
{
if (_name != value)
{
_name = value;
NotifyPropertyChanged("Name");
}
}
}
}

Este código es pesado de realizar en clases con muchas propiedades y es fácil cometer errores… además no podemos utilizar las auto-propiedades. Vamos a ver como con el sistema de intercepción de Unity podemos hacer que este evento se lance de forma automática.

1. Configurando el sistema de intercepción de Unity

Para usar el sistema de intercepción de Unity, debéis añadir los assemblies Microsoft.Practices.ObjectBuilder2, Microsoft.Practices.Unity y Microsoft.Practices.Unity.Interception a vuestro proyecto (los tres assemblies forman parte de Unity).

El primer paso es crear una clase que implemente la interfaz ICallHandler, esta clase es la encargada de proporcionarnos un punto dónde inyectar el código:

class AutoPropertyChangedHandler : ICallHandler
{
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
// Aquí podremos inyectar nuestro código
IMethodReturn msg = getNext()(input, getNext);
return msg;
}
public int Order { get; set; }
}

El siguiente paso es crear un atributo que permita indicar a Unity que ICallHandler debe usar cuando se opere con objetos de la clase. Esta clase debe derivar de HandlerAttribute y debe redefinir el método CreateHandler para devolver una instancia del ICallHandler a utilizar:

class AutoPropertyChangedAttribute : HandlerAttribute
{
public override ICallHandler CreateHandler(IUnityContainer container)
{
return new AutoPropertyChangedHandler();
}
}

El código es trivial, eh?? Simplemente devuelve un AutoPropertyChangeHandler, de esa manera Unity usará este AutoPropertyChangeHandler para todas las clases que estén decoradas con el atributo AutoPropertyChangedAttribute. De esta manera es como le indicamos a Unity qué ICallHandler debe usar por cada tipo de clase.

Finalmente queda configurar el propio contenedor. Para ello debemos debemos añadir la extensión de intercepción a Unity y indicarle que interceptor queremos utilizar para cada clase:

IUnityContainer container = new UnityContainer();
container.AddNewExtension<Interception>();
container.Configure<Interception>().SetInterceptorFor<A2>(new VirtualMethodInterceptor());

Le he indicado a Unity que para la clase A2 utilice el interceptor VirtualMethodInterceptor. Este interceptor puede interceptar todas las llamadas a métodos virtuales.

Finalmente ya podemos definir la clase A2:

[AutoPropertyChanged()]
public class A2 : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public virtual string Name { get; set; }
}

Fijaos en las diferencias entre A2 y la clase A antigua:

  1. A2 está decorada con el atributo AutoPropertyChangedAttribute que hemos definido antes.
  2. La propiedad Name es virtual
  3. No tenemos ningún código adicional para lanzar el evento PropertyChanged.

Con eso el mecanismo de intercepción está listo. Si obteneis una instancia de A2 usando el método Resolve del contenedor y miráis con el debugger de que tipo real es el objeto, veréis que no es de tipo A2, sinó de un tipo raro: el proxy que crea Unity para poder interceptar los métodos virtuales (comparad el wach para a2 y a2newed:

image

2. Implementando el código en nuestro Handler

Vamos a modificar el método Invoke de AutoNotifyPropertyHandler para que lance el evento cada vez que se llame a un set de una propiedad… La clase completa queda tal y como sigue: Básicamente en el método Invoke, miramos si el nombre del método que se ha llamado empieza por “set_”, y si es el caso asumimos que es el Setter de una propiedad. Recogemos el valor actual de la propiedad usando reflection y si no son el mismo, lanzamos el evento PropertyChanged usando reflection. Y listos! 🙂

class AutoPropertyChangedHandler : ICallHandler
{
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
bool raiseEvt = false;
string propName = null;
INotifyPropertyChanged inpc = input.Target as INotifyPropertyChanged;
if (inpc != null)
{
// Si el nombre del método empieza por "set_" es un Setter de propiedad
if (input.MethodBase.Name.StartsWith("set_"))
{
propName = input.MethodBase.Name.Substring(4);
MethodInfo getter = input.Target.GetType().GetProperty(propName).GetGetMethod();
object oldValue = getter.Invoke(input.Target, null);
object newValue = input.Arguments[0];
// Si los valores de newValue y oldValue son distintos
// debemos lanzar el evento (la comparación la hacemos por
// Equals si es posible).
raiseEvt = newValue == null ?
newValue != oldValue :
!newValue.Equals(oldValue);
}
}
IMethodReturn msg = getNext()(input, getNext);
// Si el setter no produce excepción, lanzamos el evento
if (raiseEvt && msg.Exception == null)
{
RaiseEvent(inpc, propName);
}
return msg;
}

public int Order { get; set; }

// Método que lanza el evento PropertyChanged usando reflection
private void RaiseEvent(INotifyPropertyChanged inpc, string propertyName)
{
Type type = inpc.GetType();
FieldInfo evt = null;
// Buscamos el evento (no estará en el propio Type ya que el propio Type
// será un proxy, pero iteraremos por los tipos base hasta encontrarlo)
do
{
evt = type.GetField("PropertyChanged", BindingFlags.Instance | BindingFlags.NonPublic);
type = type.BaseType;
} while (evt == null && type.BaseType != null);
// Invocamos el evento
if (evt != null)
{
MulticastDelegate mcevt = evt.GetValue(inpc) as MulticastDelegate;
if (mcevt != null)
{
mcevt.DynamicInvoke(inpc, new PropertyChangedEventArgs(propertyName));
}
}
}
}

Ahora podemos comprobar como se lanza el evento automáticamente al modificar una propiedad de A2.

Un saludo!!!!

PD: Dije en el post introductorio que no pondría código, pero finalmente os incluyo el zip con el código de este post (en SkyDrive).

PD2: Echad un post a INotifyPropertyChanged with Unity Interception AOP del blog de Dmitry Shechtman que me ha servido de inspiración para el ejemplo (yo tenía pensado uno mucho más tonto en este primer post).

Objetos que notifican sus cambios de propiedades (0/3): Introducción

Hola a todos!!! Como ha ido la despedida del 2009 y la bienvenida del 2010!!! Espero que os hayáis portado bien y que los reyes os hayan traído muuuuchos regalitos!

En este post quiero dejar de lado la serie que estaba haciendo sobre facebook connect, para ver como, gracias a Unity, podemos crear objetos que nos notifiquen cuando cambian sus propiedades, sin que nosotros debamos añadir (casi) ningún código adicional!

Pienso que es un muy buen ejemplo del poder de usar un contenedor IoC, además de resolver una situación que se da muchas veces: quiero enterarme de los cambios sobre las propiedades de un objeto, pero no quiero codificar dicho objeto de ninguna forma especial (es decir, no poner ningún tipo de código a la clase de cuyos cambios de propiedad deseo enterarme).

Donde queremos llegar…

Cuando hablamos de contenedores IoC, nos vienen a la cabeza dos grandes patrones: Dependency Injection y Service Locator… pero hay otra poderosísima razón para usarlos: las intercepciones. Esta capacidad permite “enchufar” código a los objetos en tiempo de ejecución, lo que permite añadir capacidades de AOP.

En este caso vamos a configurar la intercepción de Unity, para enchufar código cada vez que se lea/modifique una propiedad de un objeto. Dicho código nos notificará la lectura o escritura de la propiedad.

Además vamos a hacerlo configurable, de forma que no recibamos notificaciones de todas las propiedades, sino sólo de aquellas que nos interesen! La configuración de què propiedades queremos recibir notificación vamos a tenerla en otra clase. El objetivo es llegar a un código como el que sigue:

[PropertyNotifier(typeof(UfoNotifications))]
public class Ufo
{
public virtual string Name { get; set; }
public virtual int Edad { get; set; }
}

public class UfoNotifications : PropertyNotifications<Ufo>
{
public UfoNotifications()
{
this.OnProperty(x => x.Name).Set.Notify.WithParameters();
this.OnProperty(x => x.Name).Get.Notify.WithoutParameters();
this.OnProperty(x => x.Edad).Get.Notify.WithoutParameters();
}
}

El primer código nos define una clase Ufo. Es una clase totalmente normal, salvo por dos detalles:

  1. El atributo PropertyNotifier nos indica que queremos recibir notificaciones cuando se lean/modifiquen propiedades de dicha clase, y indica que la configuración sobre cuales son las propiedades que deseamos notificar se encuentra en la clase UfoNotifications
  2. Todas las propiedades son virtuales.

El segundo código contiene la configuración sobre qué propiedades y qué notificaciones queremos recibir. En este caso usamos una aproximación tipo fluent interface cuya ventaja principal es que el código es mucho más fácil de leer (y de escribir).

Esta serie va a constar de tres posts que publicaré en breve (a medida que publique los posts iré modificando éste para añadir los enlaces):

  1. Como configurar el mecanismo de intercepción de Unity para enchufar nuestro código (Añadido el 13/01/2009).
  2. Como podemos notificar los cambios de propiedades, sin obligar a que quien quiera recibirlos tenga que tener una referencia al objeto que los notifica (lo que nos impide usar eventos tradicionales de .NET) (Añadido el 14/01/2009).
  3. Como crear la fluent interface  para configurar las notificacioes

No voy a adjuntar código en cada post porque sólo tengo la solución completa implementada… en el último post adjuntaré todo el código, junto con un ejemplo. Al final sí que adjunto un zip en cada post 🙂

Nos leemos!

Cambiar el nombre de un cliente de un Workspace de TFS

Saludos! Un post cortito, cortito, cortito 🙂

Si renombramos una máquina cliente de TFS, vemos que perdemos los mappings ya que el workspace está asociado a un usuario + nombre de máquina.

Aunque podemos crearnos un workspace nuevo y borrar el antiguo también podemos modificar el workspace antiguo y cambiar el nombre de máquina, aunque para ello deberemos usar la herramienta de línea de comandos tf.exe:

tf workspaces /owner:TuUsuarioDeTFS /updateComputerName:NombreAntiguoDeLaMaquina /s:URLDelTFS

(El parámetro /owner: no se si es estrictamente necesario, yo lo he metido por si acaso).

Sí, sí… cambiar el nombre de máquina no es muy habitual, pero si se os ocurre hacerlo como yo, con n check-ins pendientes… Pues se agradece la opción 🙂

Sacado de http://blogs.msdn.com/buckh/archive/2006/03/03/update-workspace.aspx donde además dicen como modificar el workspace si lo que cambia es tu nombre de usuario.

Saludos!

PD: Esta vez ha sido cortito de veras, eh??? 😉

Ooops.. esta página no la tengo, pero tengo otra parecida para tí…

Este genial post de José M. Aguilar sobre como procesar peticiones existentes en ASP.NET MVC, me ha dado una idea que quiero compartir con vosotros… El tema consiste en que si el usuario se equivoca y entra una URL errónea como /Home/Jindex (en lugar de /Home/Index) le podemos sugerir que quizá quería ir a /Home/Index. Vamos a ver como podríamos hacerlo…

La idea es que cuando recibamos una petición errónea en el HandleUnknownAction miremos cuales son las acciones del controlador y miremos cual es la acción que más se aproxima a la acción que el usuario ha entrado.

1. Obteniendo las acciones del controlador actual

Si usamos el ActionInvoker por defecto de ASP.NET MVC, las acciones están mapeadas a métodos públicos del controlador. El nombre del método define el nombre de la acción, excepto si el método está decorado con el atributo ActionNameAttribute que especifica un nombre de acción distinto.

Así pues, una manera de obtener las acciones del controlador actual es recorrerse sus métodos públicos y obtener su nombre o bien el nombre del atributo ActionNameAttribute que tuviese asociado:

namespace System.Web.Mvc
{
public static class ControllerExtensions
{
public static IEnumerable<string> GetAllActions(this Controller self)
{
var methods = self.GetType().GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public);
return methods.Select(x =>
x.GetCustomAttributes(typeof(ActionNameAttribute), true).Length == 1 ?
((ActionNameAttribute)x.GetCustomAttributes(typeof(ActionNameAttribute), true)[0]).Name :
x.Name);
}
}
}

2. Calculando que acción es la más parecida a la que ha entrado el usuario

El siguiente paso es ver cual de todas las acciones se parece más a la acción que ha entrado el usuario. Hay varios algoritmos para calcular la distancia entre dos cadenas, uno conocido es la distancia de Levenshtein que es el que yo he usado. En el artículo de la wikipedia enlazado tenéis el pseudo-código del algoritmo… para los vagos aquí tenéis una implementación de la distancia de Levenshtein en C#.

Yo he implementado el método cómo un método de extensión de la clase string:

namespace MvcApplication1.Extension
{
public static class StringExtensions
{
public static int LevenshteinDistance(this string s, string t)
{
// Ver una implementación en http://www.merriampark.com/ldcsharp.htm
}
}
}

Finalmente en el método HandleUnknownAction sólo nos queda recorrer el enumerable de acciones devuelto por GetAllActions y para cada acción calcular la distancia de Levenshtein entre esta acción y el nombre que ha entrado el usuario… y cojer la menor:

protected override void HandleUnknownAction(string actionName)
{
var actions = this.GetAllActions();
int min = int.MaxValue;
string newAction = null;
foreach (var action in actions)
{
int ld = action.LevenshteinDistance(actionName);
if (ld < min)
{
min = ld;
newAction = action;
}
}
if (min < int.MaxValue)
{
View("RedirectView", new RedirectModel(newAction, "Home", actionName)).
ExecuteResult(this.ControllerContext);
}
else
{
base.HandleUnknownAction(actionName);
}
}

La clase RedirectModel es una clase que tiene tres propiedades: Acción a donde pensamos que el usuario quería ir, controlador de dicha acción, y acción tecleada por el usuario:

public class RedirectModel
{
public RedirectModel(string action, string controller, string originalAction)
{
this.Action = action;
this.Controller = controller;
this.OriginalAction = originalAction;
}

public string Action { get; private set; }
public string Controller { get; private set; }
public string OriginalAction { get; private set; }
}

Finalmente, sólo nos queda la vista “RedirectView”, que yo he puesto en Shared (para que pueda ser reutilizada por varios controladores):

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcApplication1.Models.RedirectModel>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
ViewUserControl1
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

<h2>Pos por <%= Model.OriginalAction%> no me viene nada...</h2>

OOppps... esta página no existe... ¿seguro que no querías ir a
<a href="<%= Url.Action(Model.Action, Model.Controller)%>"><%= Model.Action %></a>?
</asp:Content>

Y este es el resultado, si el usuario teclea /Home/Jindex esto es lo que se le muestra:

image

Ya véis, qué fácil 🙂 Un saludo!!!

PD: Os dejo un zip con la solución completa!!!

Facebook Connect (iii): Eh! Que esta parte de mi web sólo es para usuarios de facebook! (ASP.NET MVC)

Este post va a ser cortito… En los dos primeros posts de esta serie hemos visto como podemos autenticar (logon) a un usuario de facebook en nuestra web y como podemos desautenticarlo (logoff).

Yo uso ASP.NET MVC para mis desarrollos web, no voy a enumerar ahora las ventajas que en mi opinión tiene MVC sobre Webforms, sinó comentaros como podemos evitar el acceso de usuarios que no estén autenticados en facebook a ciertas regiones de nuestra web.

En ASP.NET MVC tenemos lo que se llaman filtros. A grandes rasgos un filtro es código que se ejecuta antes y/o después de cada petición y que puede alterar el comportamiento por defecto de la petición. Digamos que es un modo rápido y sencillo de aplicar técnicas AOP en aplicaciones MVC. Los filtros se enganchan a las peticiones a las que afectan mediante el uso de atributos (que se aplican a las acciones de los controladores, en MVC cada petición es atendida por una accion _método_ de un controlador).

Qué cosas podemos hacer con filtros? Pues imaginad: hacer log de las peticiones, tratar los errores que se den de una forma coherente, comprimir el resultado de la petición, o porque no, validar que el usuario esté autenticado.

Ya hay en ASP.NET MVC, un filtro que comprueba si el usuario está autenticado:

[Authorize]
public ActionResult ChangePassword()
{
ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
return View();
}

El filtro Authorize comprueba que el usuario está autenticado, y si no es el caso lo redirige a la página de login. De esta manera la acción ChangePassword sólo puede ser ejecutada por usuarios autenticados.

En mi caso, los usuarios pueden entrar en mi web usando una cuenta de facebook o bien una cuenta propia de mi web (autorización estándard de ASP.NET). Pero hay ciertas acciones que sólo están disponibles si el usuario se autentica via facebook. Así que me puse manos a la obra para crear un filtro parecido a [Authorize] pero que validase si el usuario está autenticado en facebook.

Creando el filtro…

Crear un filtro es realmente sencillo… el único punto delicado es saber que tipo de filtro queremos crear, ya que existen cuatro tipos:

  1. Authorization filters: Para implementar autorizaciones
  2. Action filters: Para implementar acciones a realizar antes o después de una acción de un controlador.
  3. Result filters: Para implementar acciones a realizar antes o después de que se haya ejecutado la vista asociada a una acción.
  4. Exception filters: Para gestionar errores producidos durante el tratamiento de una petición.

Para crearnos nuestro propio filtro derivamos de la clase FilterAttribute e implementamos una interfaz u otra. En mi caso, dado que quiero implementar un authorization filter debo implementar la interfaz IAuthorizationFilter, así que manos a la obra!

La interfaz es muy sencilla: tiene un sólo método:

void OnAuthorization(AuthorizationContext filterContext)

En este método es donde comprobaremos que el usuario está autorizado. Si no lo está usaremos las propiedades del objeto filterContext para modificar la respuesta y generar una respuesta nueva (p.ej. una redirección). En caso que el usuario esté autorizado no hacemos nada y dejamos que se procese la acción del controlador.

El código para saber si un usuario está autenticado en facebook, es muy simple:

ConnectSession cs = new ConnectSession(appKey, secretKey);
if (!cs.IsConnected())
{
// No está conectado via facebook
}

Lo que yo quiero es redirigir los usuarios no autenticados via facebook a otra acción. En lugar de redirigirlos a la acción de login (como hace [Authorize]) yo quiero poder decidir en cada caso a que acción redirigir el usuario. Es decir, poder tener algo como:

[FacebookAuthorize("Index", "Home")]
public ActionResult LinkFacebookUser()
{
return View();
}

Si el usuario no está autorizado en facebook, quiero redirigir la petición a la acción Index del controlador Home.

El código base del método OnAuthorization queda tal y como sigue:

public void OnAuthorization(AuthorizationContext filterContext)
{
string appKey = this.AppKey ?? ConfigurationManager.AppSettings["ApiKey"];
string secretKey = this.SecretKey ?? ConfigurationManager.AppSettings["Secret"];

ConnectSession cs = new ConnectSession(appKey, secretKey);
if (!cs.IsConnected())
{
// Redirigir usuario
}
}

Sólo nos queda lo más “fácil”: redirigir el usuario en caso de éste no se haya autenticado via facebook!

Redirigiendo el usuario

Para redirigir el usuario debemos crear un nuevo resultado y asignarlo a la propiedad Result del objeto filterContext recibido como parámetro. No hay ningún resultado de tipo “RedirectToActionResult”, en su lugar tenemos que usar el tipo RedirectToRouteResult, de la siguiente manera:

var rvalues = new RouteValueDictionary();
rvalues["action"] = this.Action;
if (!string.IsNullOrEmpty(this.Controller))
{
rvalues["controller"] = this.Controller;
}

filterContext.Result = new RedirectToRouteResult(rvalues);

Creamos un RouteValueDictionary y asignamos las entradas “action” y “controller” a la acción y controlador donde queremos redirigir el usuario, y a partir de este RouteValueDictionary creamos el RedirectToRouteResult que asignamos a la propiedad Result.

Y listos! Con esto ya tenemos nuestro propio filtro creado!

Os dejo aquí el código fuente completo, para que lo podáis examinar libremente!

/// <summary>
/// ActionFilter de MVC per redirigir la petició a un altre vista si l'usuari
/// no està autenticat a Facebook
/// </summary>
public class FacebookAuthorize : FilterAttribute, IAuthorizationFilter
{

/// <summary>
/// Clau pública de l'API
/// </summary>
public string AppKey { get; set; }

/// <summary>
/// Clau privada de l'API
/// </summary>
public string SecretKey { get; set; }

/// <summary>
/// Controlador al qual es transfereix la petició (<c>null</c> significa
/// el controlador actual).
/// </summary>
public string Controller { get; private set; }

/// <summary>
/// Acció a la que es transfereix la petició
/// </summary>
public string Action { get; private set; }

/// <summary>
/// Construeix un objecte <c>FacebookAuthorize</c> que redirigirà la petició
/// a l'acció <paramref name="action"/> del controlador actual si l'usuari no
/// està autenticat a facebook.
/// </summary>
/// <param name="action">Acció a la qual cal redirigir.</param>
public FacebookAuthorize(string action)
{
this.Action = action;
}

/// <summary>
/// Construeix un objecte <c>FacebookAuthorize</c> que redirigirà la petició
/// a l'acció <paramref name="action"/> del controlador <paramref name="controller" />
/// si l'usuari no està autenticat a facebook.</param>
/// </summary>
/// <param name="action">Acció a la qual cal redirigir.</param>
/// <param name="controller">Controlador al qual cal redirigir.</param>
public FacebookAuthorize(string action, string controller)
{
this.Action = action;
this.Controller = controller;
}

/// <summary>
/// Valida la petició
/// </summary>
/// <param name="filterContext">Contexte de ASP.NET MVC</param>
public void OnAuthorization(AuthorizationContext filterContext)
{
string appKey = this.AppKey ?? ConfigurationManager.AppSettings["ApiKey"];
string secretKey = this.SecretKey ?? ConfigurationManager.AppSettings["Secret"];

ConnectSession cs = new ConnectSession(appKey, secretKey);
if (!cs.IsConnected())
{
var rvalues = new RouteValueDictionary();
rvalues["action"] = this.Action;
if (!string.IsNullOrEmpty(this.Controller))
{
rvalues["controller"] = this.Controller;
}

filterContext.Result = new RedirectToRouteResult(rvalues);
}
}
}

Un saludo!!!!

PD: Al final el post no ha sido tan cortito… aiinsss! Si es que cuando empiezo a escribir no paro!! 😛

Facebook Connect (ii): Adiós, amigo adiós (o como hacer el logout).

Hola a todos! Este es el segundo post de una serie que iré haciendo contando mis experiencias con Facebook Connect. En el primer post vimos como usar facebook connect para implementar un single sign on en nuestra web (o sea que los usuarios puedan entrar en nuestra web usando el login y password de facebook).

Ahora viene la segunda parte… da igual lo buena que sea tu web, llegará un momento en que el usuario querrá irse y no creo que le guste mucho que dejemos su sesión abierta :p. Tened presente que cuando usamos connect, cuando el usuario abre la sesión en nuestra web, también la abre en facebook y viceversa: cuando cerramos la sesión en nuestra web también cerramos su sesión de facebook.

Método 1 (que no funciona)

En el post anterior, introduje FDT (Facebook Developer Toolkit), un conjunto de clases que permiten llamar a la API REST de facebook desde código C# (en lugar de usar la API javascript del propio facebook). Una de las clases que incorpora FDT es la ConnectSession, que encapsula una conexión de Facebook Connect.

Pues bien: ConnectSession tiene un método Logout, así que lo más sencillo es hacer lo siguiente:

var fbc = this.GetConnectSession();
if (fbc.IsConnected())
{
fbc.Logout();
}

¡Y listos! ¿Listos? Pues no… esto no funciona. Lo podeis comprobar fácilmente: si sin cerrar el navegador abrís otra pestaña y os vais a facebook, veréis que entráis directamente sin que os pida las credenciales. Todavía estáis autenticados en facebook.

Después de hacer algunas pruebas lo siguiente que hice, fue navegar por el código fuente de FDC y mirar que hacia el método Logout de la clase ConnectSession, y eso es lo que me encontré:

/// <summary>
/// Logs out user
/// </summary>
public override void Logout()
{
}

Vaya… pues claro que no me funcionaba! ¿Porque está vacío este método? Bueno, pues FDT proporciona distintos tipos de sesiones para dar soporte a varias funcionalidades (p.ej. aquí nos centramos en Connect, pero se puede usar FDT para desarrollar aplicaciones que funcionen dentro de facebook). Según sea el uso que le demos a FDT usaremos una clase de sesión u otra, pero todas derivan de FacebookSesion, clase abstracta que define todos los métodos que todas las sesiones deben implementar. El problema está en que algunos métodos no pueden ser implementados en según que clases derivadas, debido a que Facebook no ofrece las mismas funcionalidades según el tipo de aplicación que estemos desarrollando…

… no se si me he explicado bien! Resumiendo: Facebook no ofrece API REST para cerrar una sesión de Connect. Por eso la clase ConnectSession no implementa el método Logout. Quizá un throw new NotImplementedException hubiese sido mejor, ya que almenos daría una pista visual y rápida de lo que realmente ocurre, en lugar de tener un método vacío que no hace nada…

Método 2 (el que sí funciona)

Para hacer el logout del usuario, debemos usar el API Javascript de Facebook. De hecho, básicamente, debemos llamar al método FB.Connect.logout, sólo que hay que tener presentes un par de puntos:

  1. Antes de llamar a FB.Connect.logout debemos llamar a FB.Init para inicializar el API de Facebook
  2. Antes de llamar a FB.Init debemos llamar a FB_RequireFeatures para decidir que parte del API de facebook queremos inicializar.

FB_RequireFeatures es una función asíncrona, pero por suerte le podemos pasar el método a ejecutar cuando la ejecución haya tenido lugar, así que lo más normal es tener algo como:

function PerformFBLogout() {
FB_RequireFeatures(["XFBML"], function() {
FB.init("TU_CLAVE_DEL_API", "/xd_receiver.htm");
FB.Connect.logout(function() {
window.location="<%= Url.Action("LogOff","Account") %>";
});
});
}

Este código llama a FB_RequireFeatures, y le pasa la función a ejecutar cuando FB_RequireFeatures se haya ejecutado asíncronamente. Entonces podemos llamar a FB.Init y posteriormente a FB.Connect.logout. A FB.Connect.logout le pasamos una nueva función con el código a ejecutar cuando el usuario se haya desconectado. En mi caso cuando el usuario se haya desconectado realizo una redirección a la acción “LogOff” del controlador “Account” (sí, yo uso ASP.NET MVC). En la acción LogOff del controlador Account desautentico el usuario.de ASP.NET y muestro la vista de inicio.

Para que este código funcione, debe estar en una página que tenga el siguiente tag script en el body (no en el head):

<script src="http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php" type="text/javascript"></script>

(Yo este tag script lo tengo colocado en mi página .master)

Bueno… pues eso es todo por el momento! Ya os iré contando más batallitas con Connect!!! 😉

Facebook Connect: Si estás en facebook bienvenido a mi web…

Antón Molleda comentaba en un post de su blog ([WLMT] Socializándonos), las ventajas que ofrece integrar nuestras aplicaciones an alguna de las redes sociales existentes. Él comentará sus experiencias con el Windows Live Messenger Toolkit en su blog, así que yo voy a comentaros cuatro cosillas sobre Facebook Connect, el mecanismo de integración que nos ofrece Facebook.

Con Facebook Connect lo que obtenemos es la posibilidad de que los usuarios registrados en Facebook puedan entrar con su login en nuestra web (o aplicación, aunque yo me cocentraré en web), y no sólo eso, sinó que (previa aceptación del usuario) nuestra aplicación pueda publicar contenido en facebook.

En el caso que me ocupa estoy desarrollando una web y me interesa que los usuarios que están en facebook puedan entrar en ella sin necesidad de registrarse en mi web. Mi web seguirá manteniendo su propio sistema de registro de usuarios, puesto que no es obligatorio tener cuenta en facebook para entrar en ella. La página de login tendría un aspecto tal como (queda prohibido cualquier comentario sobre la estética :p):

image

1. Crear una aplicación en Facebook para Connect

El primer paso para poder usar Facebook Connect es crear la aplicación facebook que nos va a dar acceso a facebook. Para ello, entramos en facebook y seguimos los siguientes pasos:

  • Nos dirigimos a http://www.facebook.com/developers/createapp.php para crear una nueva aplicación facebook.
  • En el apartado Basic entramos el nombre de la aplicación. El resto de valores los podemos dejar por defecto (aunque si soys detallistas siempre podéis añadir un icono a vuestra aplicación :p). Ahora viene lo importante…
  • En el apartado Connect debéis entrar el dominio donde está vuestra web… Si estais desarrollando seguramente no tendréis vuestra web colgada. Según tengo entendido localhost no és un dominio válido, así que os “inventais” un nombre y entrais en el archivo hosts este nombre con la ip 127.0.0.1. Además si vuestro servidor web no escucha por el puerto 80 (lo normal cuando estamos con VS) debemos entrar el número de puerto que vamos a usar y configurar VS para que arranque cassini siempre con el mismo puerto. P.ej. en el apartado Connect de mi aplicación facebook yo tengo entrado http://wofserver:12345:

image

Y luego tengo el VS configurado para que me arranque cassini siempre por el puerto 12345:

image

Y recordad la entrada en el fichero hosts para mapear vuestro “dominio” al 127.0.0.1

  • En el apartado Basic de vuestra aplicación hay dos identificadores que vamos a necesitar:
    • API Key: Es la clave pública de la aplicación y que debe usarse en la gran mayoría de las llamadas al API de facebook
    • Secret: La clave secreta. Debe usarse en algunas llamadas al API, y como su nombre indica no debemos compartirla con nadie 😉
  • Una vez creada la aplicación, estamos listos para interactuar con facebook.

2. Preparando nuestra aplicación web para hablar con facebook

La clave para habilitar la comunicación con facebook está en un archivo html llamado xd_receiver.htm. El código de dicho archivo es tal y como sigue (siempre es igual):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>xd</title>
</head>
<body>

<script src="http://static.ak.facebook.com/js/api_lib/v0.4/XdCommReceiver.js" type="text/javascript"></script>

</body>
</html>

Podemos poner el archivo donde queramos, pero yo os recomiendo que lo pongáis en el directorio raíz de vuestra web. Es decir en mi caso http://wofserver:12345/xd_receiver.htm

 

 

 

 

 

3. Preparando el entorno para renderizar XFBML

Para ayudarnos a integrarnos con facebook, éste incorpora una API de tags conocida como XFBML (XHtml FaceBook Markup Language). Usando estos tags podemos p.ej. renderizar el botón de login, o el nombre del usuario conectado.

Para ello debemos indicar que nuestras páginas son XHTML e importar el espacio de nombres fb que es el que usan los tags XFBML. Para ello modificamos las etiquetas <html> y <!DOCTYPE>:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:fb="http://www.facebook.com/2008/fbml">

Ya casi estamos… sólo nos queda referenciar el uso del fichero javascript que habilita el API de facebook (así como XFBML), para ello debemos añadir el siguiente tag <script> justo después de abrir el tag <body>:

<script src="http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php" type="text/javascript"></script>

Evidentemente os recomiendo utilizar una master page para tener todos estos cambios en todas vuestras páginas (estos tags deben estar en cualquier página que quiera usar el API de facebook).

4. Usando XFBML

Antes de poder usar XFBML hemos de incializar nuestra conexión con facebook y habilitar XFBML. Yo tengo el código puesto en la pantalla de login, pero esto ya dependerá de cada uno. Para ello debemos usar el siguiente código javascript:

<script type="text/javascript">
FB_RequireFeatures(["XFBML"], function() {
FB.Facebook.init("CLAVE_API", "/xd_receiver.htm");
});
</script>

Ahora sí: el uso de XFBML es super simple. P.ej. para renderizar el botón de login basta con usar el tag <fb:login-button>:

<fb:login-button v="2" size="medium" 
onlogin="window.location.reload(true);">
Login WoF with facebook
</fb:login-button>

En este caso se renderiza el botón de login, y cuando el usuario se haya identificado se forzará un refresco de la página. El texto que colocamos dentro del tag <fb:login-button> será el texto del botón:

image

Hay muchos tags XFBML, en esta dirección (http://wiki.developers.facebook.com/index.php/XFBML) tenéis la lista completa.

 

Cuando el usuario haga click en el botón de iniciar sesión en facebook pueden ocurrir dos cosas:

  1. Que el usuario ya estuviese identificado en facebook. En este caso se disparará el evento onlogin del botón.
  2. Que el usuario no estuviese identificado en facebook. En este caso le saldrá la ventana para que se identifique. Una vez identificado se disparará el evento onlogin:

image 

5. Accediendo a información del usuario

Llegados a este punto ya nos hemos integrado con el login del usuario… Pero esto no sirve de mucho si no somos capaces de extraer información del usuario (p.ej su nombre, o bien si ya había un usuario identificado en facebook). Para ello podemos usar el API de facebook (obviamente basada en javascript), pero yo voy a contaros como se puede hacerlo usando el “Facebook Developer Toolkit” que os podéis descargar de Codeplex.

Una vez descargado basta con referenciar el assembly “Facebook.dll”, y luego podemos utilizar la clase ConnectSession para averiguar si un usuario está conectado en facebook:

var fbc= new ConnectSession("<CLAVE_API>","<CLAVE_SECRETA>");
if (fbc.IsConnected())
{
// Usuaro conectado en facebook. En fbc.UserId tenemos el ID unico del
// usuario en facebook.
}
else
{
// No hay usuario conectado en facebook
}

A partir de aquí, lo que hagamos ya dependerá de nuestras necesidades. P.ej. en mi caso, pregunto al usuario se desea usar mi web con su login de facebook y si responde afirmativamente creo un usuario especial en mi tabla de usuarios, vinculado a dicho id de facebook y autentico este usuario en mi sitio web…

Espero que este post os haya servido para daros una idea de como funciona Facebook Connect. En siguientes posts espero ir contando alguna cosilla más al respecto! 😉

Saludos!!!