Para una clase dada, me gustaría tener una funcionalidad de rastreo, es decir, me gustaría registrar cada llamada a método (firma de método y valores de parámetros reales) y cada salida de método (solo la firma de método).
¿Cómo logro esto asumiendo que:
- No quiero usar ninguna biblioteca AOP de terceros para C #,
- No quiero agregar código duplicado a todos los métodos que quiero rastrear,
- No quiero cambiar la API pública de la clase: los usuarios de la clase deberían poder llamar a todos los métodos exactamente de la misma manera.
Para hacer la pregunta más concreta, supongamos que hay 3 clases:
public class Caller
{
public static void Call()
{
Traced traced = new Traced();
traced.Method1();
traced.Method2();
}
}
public class Traced
{
public void Method1(String name, Int32 value) { }
public void Method2(Object object) { }
}
public class Logger
{
public static void LogStart(MethodInfo method, Object[] parameterValues);
public static void LogEnd(MethodInfo method);
}
¿Cómo invoco Logger.LogStart y Logger.LogEnd para cada llamada a Method1 y Method2 sin modificar el método Caller.Call y sin agregar las llamadas explícitamente a Traced.Method1 y Traced.Method2 ?
Editar: ¿Cuál sería la solución si se me permite cambiar ligeramente el método de llamada?
c#
reflection
aop
Oficial
fuente
fuente
Respuestas:
C # no es un lenguaje orientado a AOP. Tiene algunas características de AOP y puede emular algunas otras, pero hacer AOP con C # es doloroso.
Busqué formas de hacer exactamente lo que querías hacer y no encontré una manera fácil de hacerlo.
Según tengo entendido, esto es lo que quieres hacer:
y para hacer eso tienes dos opciones principales
Herede su clase de MarshalByRefObject o ContextBoundObject y defina un atributo que herede de IMessageSink. Este artículo tiene un buen ejemplo. No obstante, debe tener en cuenta que al usar un MarshalByRefObject, el rendimiento disminuirá como el infierno, y lo digo en serio, estoy hablando de un rendimiento 10x perdido, así que piense cuidadosamente antes de intentarlo.
La otra opción es inyectar código directamente. En tiempo de ejecución, lo que significa que tendrá que usar la reflexión para "leer" cada clase, obtener sus atributos e inyectar la llamada apropiada (y para el caso creo que no podría usar el método Reflection.Emit como creo que Reflection.Emit wouldn no le permite insertar nuevo código dentro de un método ya existente). En tiempo de diseño, esto significará crear una extensión para el compilador CLR que honestamente no tengo idea de cómo se hace.
La última opción es usar un marco de IoC . Tal vez no sea la solución perfecta, ya que la mayoría de los marcos de IoC funcionan definiendo puntos de entrada que permiten enganchar los métodos, pero, dependiendo de lo que desee lograr, puede ser una aproximación justa.
fuente
Reflection.Emit
. Este es el enfoque elegido por Spring.NET . Sin embargo, esto requeriría métodos virtualesTraced
y no es realmente adecuado para su uso sin algún tipo de contenedor IOC, por lo que entiendo por qué esta opción no está en su lista.La forma más sencilla de lograrlo es usar PostSharp . Inyecta código dentro de sus métodos en función de los atributos que le aplica. Te permite hacer exactamente lo que quieres.
Otra opción es usar la API de creación de perfiles para inyectar código dentro del método, pero eso es realmente duro.
fuente
Si escribe una clase, llamada Tracing, que implementa la interfaz IDisposable, podría ajustar todos los cuerpos de los métodos en un
En la clase Tracing puede manejar la lógica de las trazas en el método constructor / Dispose, respectivamente, en la clase Tracing para realizar un seguimiento de la entrada y salida de los métodos. Tal que:
fuente
Puede lograrlo con la función de intercepción de un contenedor DI como Castle Windsor . De hecho, es posible configurar el contenedor de tal manera que todas las clases que tengan un método decorado por un atributo específico sean interceptadas.
Con respecto al punto 3, OP solicitó una solución sin el marco AOP. Asumí en la siguiente respuesta que lo que debería evitarse eran Aspect, JointPoint, PointCut, etc. De acuerdo con la documentación de Interceptación de CastleWindsor , ninguno de ellos debe cumplir lo que se le pide.
Configure el registro genérico de un interceptor, en función de la presencia de un atributo:
Agregue el IContributeComponentModelConstruction creado al contenedor
Y puedes hacer lo que quieras en el propio interceptor
Agregue el atributo de registro a su método para iniciar sesión
Tenga en cuenta que será necesario cierto manejo del atributo si solo necesita interceptarse algún método de una clase. Por defecto, todos los métodos públicos serán interceptados.
fuente
Si desea rastrear sus métodos sin limitación (sin adaptación de código, sin marco AOP, sin código duplicado), déjeme decirle que necesita algo de magia ...
En serio, lo resolví para implementar un AOP Framework que funcionara en tiempo de ejecución.
Puede encontrar aquí: NConcern .NET AOP Framework
Decidí crear este Marco de AOP para dar respuesta a este tipo de necesidades. Es una biblioteca simple muy ligera. Puede ver un ejemplo de registrador en la página de inicio.
Si no desea utilizar un ensamblado de terceros, puede explorar el código fuente (código abierto) y copiar ambos archivos Aspect.Directory.cs y Aspect.Directory.Entry.cs para adaptarlos según sus deseos. Estas clases permiten reemplazar sus métodos en tiempo de ejecución. Solo le pediría que respete la licencia.
Espero que encuentre lo que necesita o para convencerlo de que finalmente use un Marco de AOP.
fuente
Eche un vistazo a esto: cosas bastante pesadas ... http://msdn.microsoft.com/en-us/magazine/cc164165.aspx
Essential .net - don box tenía un capítulo sobre lo que necesita llamado Intercepción. Raspé un poco aquí (Perdón por los colores de fuente, tenía un tema oscuro en ese entonces ...) http://madcoderspeak.blogspot.com/2005/09/essential-interception-using-contexts.html
fuente
He encontrado una forma diferente que puede ser más fácil ...
Declarar un método InvokeMethod
Luego defino mis métodos así
Ahora puedo tener la verificación en tiempo de ejecución sin la inyección de dependencia ...
No hay problemas en el sitio :)
Esperemos que acepte que esto tiene menos peso que un AOP Framework o que se deriva de MarshalByRefObject o que utiliza clases remotas o proxy.
fuente
Primero tiene que modificar su clase para implementar una interfaz (en lugar de implementar MarshalByRefObject).
A continuación, necesita un objeto contenedor genérico basado en RealProxy para decorar cualquier interfaz que permita interceptar cualquier llamada al objeto decorado.
Ahora estamos listos para interceptar llamadas al Método 1 y al Método 2 de ITraced
fuente
Puede utilizar el marco de código abierto CInject en CodePlex. Puede escribir un código mínimo para crear un inyector y hacer que intercepte cualquier código rápidamente con CInject. Además, como se trata de código abierto, también puede ampliarlo.
O puede seguir los pasos mencionados en este artículo sobre Llamadas al método de interceptación usando IL y crear su propio interceptor usando las clases Reflection.Emit en C #.
fuente
No conozco una solución, pero mi enfoque sería el siguiente.
Decora la clase (o sus métodos) con un atributo personalizado. En otro lugar del programa, deje que una función de inicialización refleje todos los tipos, lea los métodos decorados con los atributos e inyecte algún código IL en el método. En realidad, podría ser más práctico reemplazar el método por un trozo que llame
LogStart
, el método real y luegoLogEnd
. Además, no sé si puede cambiar los métodos usando la reflexión, por lo que podría ser más práctico reemplazar todo el tipo.fuente
Potencialmente, podría utilizar el patrón decorador GOF y 'decorar' todas las clases que necesitan seguimiento.
Probablemente solo sea realmente práctico con un contenedor de IOC (pero como apunta antes, es posible que desee considerar la intercepción del método si va a seguir el camino del IOC).
fuente
necesita molestar a Ayende para obtener una respuesta sobre cómo lo hizo: http://ayende.com/Blog/archive/2009/11/19/can-you-hack-this-out.aspx
fuente
AOP es imprescindible para la implementación de código limpio, sin embargo, si desea rodear un bloque en C #, los métodos genéricos tienen un uso relativamente más fácil. (con sentido de inteligencia y código fuertemente tipado) Ciertamente, NO puede ser una alternativa para AOP.
Aunque PostSHarp tiene pequeños problemas con errores (no me siento seguro para usar en producción), es algo bueno.
Clase genérica de envoltura,
el uso podría ser así (con sentido de inteligencia, por supuesto)
fuente
fuente
Lo mejor que puede hacer antes de C # 6 con 'nameof' lanzado es usar StackTrace lento y Expresiones linq.
Por ejemplo, para tal método
Tal línea puede ser producida en su archivo de registro
Aquí está la implementación:
fuente