Desenmascarando la CSLA.Net

Después de mucho tiempo sin escribir en español, quería hacer un post sobre algo que estuvimos viendo en la VAN de altnet hispano de ayer.
Mas allá de no gustarme muchas cosas sobre CSLA.Net de lo que voy hablar en este post es muy particular. Lo que voy a comentar es sobre el manejo de propiedades que es como sigue:

private static PropertyInfo<string> LastNameProperty = 
  RegisterProperty<string>(p=>p.LastName);
private string _lastName = LastNameProperty.DefaultValue;
public string LastName
{
  get
  {
    return GetProperty(LastNameProperty, _lastName);
  }
  set
  {
    SetProperty(LastNameProperty, ref _lastName, value);
  }
}

Cada vez que veo algo así, me pregunto que es lo que “gano” con hacer eso? Y créanme que lo he visto en otro lugar. La respuesta suele ser diferente, a veces la función “SetProperty” en la clase base suele hacer cosas como:

  • Disparar el evento de la interfaz INotifyProperyChanged.
  • Validar algo.
  • Establecer en algún lugar que la instancia esta Dirty, es decir con cambios pendientes de persistir.
  • También para implementar el comportamiento de IEditableObject.
  • Logging.
  • Validar acceso a alguna propiedad.

Y no mucho mas que eso. No es que todas estas cosas no sean necesarias, lo que me saca de las casillas es la forma de declarar una propiedad. Tiene “MUCHO” para hacer tan “POCO”. A tal punto que definir una propiedad de mi BussinesObject puede ser un verdadero dolor de cabeza y necesite un generador de código. Por supuesto, se comento en la VAN que si a alguien no le gusta esto, lo puede hacer “manual” que son muchas mas líneas de código.
Todas las cosas que mencione en la lista anterior ya han sido estudiadas y pueden ser realizadas perfectamente mediante AOP. La solución, como dijo Fabio Maulo ayer es utilizar PostSharp o DynamicProxy. Estos frameworks permiten implementar el concepto de AOP, de dos maneras muy diferentes, por un lado DynamicProxy a partir de una clase genera dinámicamente otra que hereda (en algunos casos no) de la primera y ejecuta los interceptors. Por otro lado PostSharp inyecta código post compilación. Mientras que PostSharp es muy potente, DynamicProxy es bastante flexible y fácil de entender para alguien que se inicie en AOP.
Como conozco poco de PostSharp, voy a explicar un poco DP. DynamicProxy de Castle (como Spring.AOP de Spring y creo que en la misma línea esta LinFu) son muy, pero muy sencillos de interpretar.
Cuando uno crea un proxy lo que se tiene que imaginar es que “una pila de interceptores con un orden especifico va a interceptar cada llamada a función de la clase que se esta proxy-ando”.´

Algo así:
first_thumb[20] 

La interfaz IInterceptor tiene solamente un método:

public void Intercept(IInvocation invocation)

IInvocation es un objeto muy jugoso. Tiene muchas propiedades entre las que se destacan:

  • Arguments: un array con los parámetros que se llamo al método. Recordemos que las propiedades al final son métodos.
  • Method: es un objeto MethodInfo propio del framework, del cual podemos sacar cosas interesantísimas como el nombre del método, los attributos, custom attributes, etc.
  • Proxy: la instancia del objeto proxy.
  • InvocationTarget: el destino final de la invocación.
  • ReturnValue: Esta es una de las mas importantes…. Este es el valor que va a devolver la función. Puedo cambiarlo, inspeccionarlo, guardarlo, hacer lo que quiera.

Luego tiene un método que se llama “Proceed”, este método lo que hace es proceder con la invocación, esto significa pasar la llamada al siguiente interceptor o finalmente al destino de la invocación.

Escribiendo un IInterceptor para NotifyPropertyChanged. Lo primero que hace falta es un buen test ;-)

[TestFixture]
public class Test1
{
    [Test]
    public void notify_on_change()
    {
        var proxyGen = new ProxyGenerator();
        bool eventWasRaised = false;
        var persona = (Persona)proxyGen.CreateClassProxy(typeof (Persona),
                               new []{typeof(INotifyPropertyChanged)},
                               new IInterceptor[] {new PropertyChangeInterceptor()});

        ((INotifyPropertyChanged) persona).PropertyChanged += 
            (sender, args) =>
            {
                args.PropertyName.Should().Be.EqualTo("Nombre");
                eventWasRaised = true;
            };

        persona.Nombre = "José";

        eventWasRaised.Should().Be.True();

    }
}

public class Persona
{
    public virtual int Id { get; set; }
    public virtual string Nombre { get; set; }
    public virtual string Apellido { get; set; }
}

Notar como esta declarada la clase “Persona”, solo lo que necesito. Por la forma en que esta instanciada no se preocupen mucho por que se puede hacer unos extensions methods, y dejarla dentro del contenedor, el contenedor resolverá el Proxy. Generalmente Castle, Spring y LinFu permiten lo mismo, el generador de proxy se integra con el container.

Explicando el test, lo que estoy haciendo es crear una instancia proxy-ada, subscribirme al evento propertychanged y modificar la propiedad Nombre. Lo que verifico es que el evento haya sido disparado y que en los args venga el nombre de la property que modifique.

Como hago que este test pase?

public class PropertyChangeInterceptor : IInterceptor
{
    private PropertyChangedEventHandler _handler;

    #region IInterceptor Members

    public void Intercept(IInvocation invocation)
    {
        string methodName = invocation.Method.Name;

        if (methodName == "add_PropertyChanged")
        {
            _handler = (PropertyChangedEventHandler)
                       Delegate.Combine(_handler, (Delegate) invocation.Arguments[0]);
            return;
        }
        if (methodName == "remove_PropertyChanged")
        {
            _handler = (PropertyChangedEventHandler)
                       Delegate.Remove(_handler, (Delegate) invocation.Arguments[0]);
            return;
        }

        invocation.Proceed();

        if (invocation.MethodInvocationTarget.Name.StartsWith("set_"))
        {
            if (_handler != null)
              _handler(invocation.Proxy, new PropertyChangedEventArgs(methodName.Substring(4)));
            }
        }
    }

    #endregion
}

Lo que hago en esta clase es primero interceptar la subscripción y dessubscripción al evento “PropertyChanged”. Notar que a esas invocaciones no les doy proseguir, puesto que el destino de la invocación ni si quiera implementa INotifyPropertyChanged (en el test la interfaz fue agregada al proxy como una interface adicional). Para todas las demás llamadas las prosigo. La parte fundamental ocurre después de proseguir la invocación, si el método empezaba con “set_” asumo que el código que llama esta seteando una propiedad, por lo tanto disparo el evento, pasándole como argumentos el nombre del método sin los primeros 4 caracteres (sin el “set_”).

De esta forma dejo librado a su imaginación las cosas que pueden hacer con los interceptors. Aunque muchas de las que vienen con la CSLA.Net ya existen de esta forma o de otras formas mejores.

Creo que el código para hacer el bussines object verdaderamente se disminuye.

Espero que el artículo haya sido de vuestro agrado y espero sus comentarios.


blog comments powered by Disqus
  • Categories

  • Archives