Hace tiempo que tenía la idea de hacer un programa para poder inyectar código sobre assemblies ya compiladas dando vueltas por la cabeza. Finalmente, hoy fue el día de crear su versión 0.1
Les presento zInject!
Cual es la idea detrás de esto? Simple...
Supongamos que yo tengo un proyecto hecho y derecho, con una clase cuyos métodos son M1, M2 y M3.
public class M
{
public void M1()
{
...
}
public void M2()
{
...
}
public void M3()
{
...
}
}
Un día viene el jefe y me dice: "De ahora en mas, tenemos que verificar cuanto demora la ejecución de los siguientes métodos: M1, M2 y M3.
En ese momento uno piensa: "Bien, tendría que hacer nuevos métodos que se ejecuten antes y después de M1, M2 y M3 y que los mismos calcules cuanto tardó cada ejecución.
public class M
{
public void M1()
{
Empezo("M1");
...
Termino("M1");
}
public void M2()
{
Empezo("M2");
...
Termino("M2");
}
public void M3()
{
Empezo("M3");
...
Termino("M3");
}
}
Esto trae una serie de problemas. Agregar esto ensucia el código de M1, M2 y M3 por las llamadas a los nuevos métodos al principio y al final. Peor aún, si tengo que agregar nuevos "comportamientos" al principio o al final del método, el código termina siendo incomprensible! Ni hablar en caso de que cada comportamiento (como seguridad o auditoria) a agregar implique mas de una sola linea de código...
Para solucionar llega zInject.
Para usarlo, definimos un atributo que hereda de InjectionBaseAttribute
public class TiempoAttribute : InjectionBaseAttribute
{
public TiempoAttribute()
{
}
public override void BeforeExecute(string method, object target)
{
Empezo(method);
}
public override void AfterExecute(string method, object target)
{
Termino(method);
}
}
Después, simplemente agregamos el atributo a los métodos que se desea:
public class M
{
[Tiempo]
public void M1()
{
...
}
[Tiempo]
public void M2()
{
...
}
[Tiempo]
public void M3()
{
...
}
}
Ahora, dirán... Todo muy lindo, pero... ¿Cómo se ejecuta el código del atributo?
Para poder hacer esto hay que ejecutar el injector propiamente dicho. Esto se puede hacer tanto ejecutándolo desde linea de comandos una vez compilado el assembly, como usando una Post Build Action dentro de Visual Studio. Esta utilidad agregará dentro del IL del assembly las llamadas necesarias a los metodos ExecuteBefore y ExecuteAfter
To Do:
- Evitar totalmente el uso de Reflection. Actualmente, el código inyectado utiliza reflection para ejecutar las acciones de los atributos, simplemente por simplicidad del desarrollo. A futuro, se inyectará directamente la llamda al atributo concreto.
- Poder customizar cuales atributos aplicar y cuales no
- UI para Windows/OS X/Linux
- Quitar dependencias contra librerías externas
Les dejo la dirección del proyecto en CodePlex para bajar los binarios compilados y el código completo para bajarlo con sus tests para bajarlo y ver como se usa.
Cualquier sugerencia es bienvenida!
Saludos!
Zaiden