Subsonic

Bug en Subsonic 3.x al comparar instancias de objetos

He utilizado Subsonic de Rob Conery por varios años, desde sus primera version.

Es un ORM decente, hecho por un buen desarrollador. Subsonic ya no se desarrolla activamente (ahora se enfoca en Massive, su micro ORM), pero la ultima version, 3.0.0.4, sigue siendo utilizada por varios proyectos.

Hace algunas semanas desarrolle una funcionalidad nueva en un proyecto existente, que utilizaba Subsonic, y al estar probando la funcionalidad descubri un bug en Subsonic y la manera en que compara instancias de objetos de la misma clase, haciendo que siempre regrese false en una comparacion.

El codigo problema esta en el metodo Equals, generado por el template T4 de ActiveRecord de Subsonic:

public override bool Equals(object obj)
{
    if(obj.GetType()==typeof(Entity))
    {
        Entity compare=(Entity)obj;
        return compare.KeyValue()==this.KeyValue();
    }
    else
    {
        return base.Equals(obj);
    }
}

Y el metodo KeyValue hace esto:

public object KeyValue()
{
    return this.EntityId;
}

Al estar comparando dos objetos, este metodo es llamado, y si los objetos que se estan comparando son de la misma clase, ejecuta la linea

    return compare.KeyValue()==this.KeyValue();

El metodo KeyValue() simplemente regresa el valor del campo que este asignado como Id de la clase, ya sea un entero, un GUID, long, etc. El problema es que regresa el valor casteandolo a tipo object. Es decir, la siguiente comparacion regresara false, incorrectamente:

    bool testOne = (object)5 == (object)5; //this will return false

No importa que estamos comparando un valor constante, porque lo estamos casteando a ser de tipo object. La comparacion correcta seria comparar los objetos directamente (sin castearlos), o utilizando el metodo Equals de la misma clase.


    bool testTwo = 5.Equals(5); //this will return true, as we expect.

Entonces, para corregir el bug en Subsonic, se puede actualizar el template T4 de la siguiente manera:

public override bool Equals(object obj)
{
    if(obj.GetType()==typeof(<#=tbl.ClassName#>))
    {
        <#=tbl.ClassName#> compare=(<#=tbl.ClassName#>)obj;
        return compare.KeyValue().Equals(this.KeyValue());
    }
    else
    {
        return base.Equals(obj);
    }
}

Es decir, reemplazando la comparacion anterior (que usaba el operador ==) con una llamada explicita al metodo Equals de la instancia del objeto que estamos comparando.

Espero les sirva.

Los archivos .tt (T4) no funcionan en proyectos tipo Website

Visual Studio 2008 Logo

Visual Studio 2008 Logo

Ya hace meses que no configuraba Subsonic desde cero en un proyecto, y por lo visto la última vez que lo hice, fue Subsonic 3.0 para un proyecto de Windows Forms.

Hago este post porque no está señalado en letras grandes en la pagina de Subsonic (supongo que esperan que todo mundo lo sepa de antemano), pero los archivos .tt que hay que arrastrar como parte del setup de SubSonic, simplemente no funcionan en proyectos de tipo Website.

Es decir, si queremos utilizar SubSonic 3.0 en un proyecto de tipo Website, la solucion es agregar un proyecto de tipo Class Library, arrastrar los archivos .tt a ese proyecto, y luego hacer referencia a la Class Library desde el proyecto tipo Website.

Hay otra solucion que es utilizar la Command-Line Tool  for Text Templates (de Microsoft), y en el siguiente artículo explican como usarla en Website projects: Visual Studio Website Projects: Add context menu for T4 files. El unico problema con esta alternativa es que el context menu que se agrega, se muestra para todos los tipos de archivos en Visual Studio, no solo los archivos T4.

Como referencia al problema, les dejo una liga a una pregunta en StackOverflow.com, en la que mencionan algunos de los principales problemas con los que se topa la gente al estar configurando Subsonic. De hecho, uno de los comentarios que hacen es precisamente que deberían de especificar esto de los .tt’s en Website projects desde el inicio.

Post: SubSonic 3 Installation doesn’t work?

 Scroll to top