c# - Implementación básica de AOP como atributo utilizando.NET Framework estándar
attributes (4)
Posible duplicado:
Método de ajuste de C # a través de atributos
Me gustaría lograr tal funcionalidad:
[Atomic]
public void Foo()
{
/* foo logic */
}
Donde el atributo [Atomic]
es un atributo, que ajusta la lógica de la función dentro de un ámbito de transacción:
using(var scope = new TransactionScope())
{
/* foo logic */
scope.Complete();
}
¿Cómo escribir tal atributo?
Anteriormente hice la misma question . Sé que esto se puede hacer usando AOP, pero no mencioné que estoy buscando una implementación de prueba de concepto más simple o artículos útiles que puedan ayudarme a escribir esto usando .NET puro Marco (supongo que uso los tipos RealProxy
y MarshalByRefObject
, sobre los cuales he leído preguntas relacionadas con la navegación).
Necesito resolver exactamente este ejemplo mostrado. Parece algo básico, así que quiero aprender cómo hacerlo desde cero. No necesita ser seguro y flexible por ahora.
Parece una cosa básica ...
Es una de las (muchas) cosas que son simples de entender, pero que no son fáciles de implementar.
Según la respuesta de Oded , los atributos en .NET no hacen nada . Solo existen para que otros códigos (o desarrolladores) puedan verlos más adelante. Piensa en ello como un comentario elegante.
Con eso en mente, puedes escribir tu atributo de esta manera
public class AtomicAttribute : Attribute { }
Ahora, en la parte difícil, debes escribir un código para buscar ese atributo y cambiar el comportamiento del código.
Dado que C # es un lenguaje compilado, y dadas las reglas de .NET CLR, hay teóricamente 3 formas de hacerlo
Enganche en el compilador de C # y haga que emita un código diferente cuando vea ese atributo.
Esto parece que sería bueno, pero simplemente no es posible en este momento. Quizás el proyecto Roslyn pueda permitir esto en el futuro, pero por ahora, no puede hacerlo.Escriba algo que escanee el ensamblado .NET después de que el compilador de C # lo haya convertido a MSIL y cambie el MSIL.
Esto es básicamente lo que hace PostSharp . Escanear y reescribir MSIL es difícil . Hay bibliotecas como Mono.Cecil que pueden ayudar, pero sigue siendo un problema muy difícil. También puede interferir con el depurador, etc.Use la API de perfiles de .NET para monitorear el programa mientras se está ejecutando, y cada vez que vea una llamada de función con ese atributo, rediríjalo a alguna otra función de envoltura.
Esta es quizás la opción más simple (aunque todavía es muy difícil ), pero el inconveniente es que su programa ahora debe ejecutarse bajo el generador de perfiles. Esto puede estar bien en su PC de desarrollo, pero causará un gran problema si intenta implementarlo. Además, es probable que haya un gran impacto en el rendimiento al utilizar este enfoque.
En mi opinión, lo mejor que puede hacer es crear una función de envoltura que configure la transacción y luego pasarla a un lambda que haga el trabajo real. Me gusta esto:
public static class Ext
{
public static void Atomic(Action action)
{
using(var scope = new TransactionScope())
{
action();
scope.Commit();
}
}
}
.....
using static Ext; // as of VS2015
public void Foo()
{
Atomic(() => {
// foo logic
}
}
El término sofisticado de informática para esto es la programación de orden superior.
Los atributos son metadatos, eso es todo lo que son.
Existen muchas herramientas que pueden aprovechar estos metadatos, pero estas herramientas deben conocer el atributo.
Las herramientas AOP como PostSharp leen dichos metadatos para saber qué y dónde tejer aspectos en el código.
En resumen, solo escribiendo un AtomicAttribute
le dará nada ; deberá pasar el ensamblaje compilado a través de una herramienta que conoce este atributo y hacer "algo" para lograr el AOP.
No es una cosa básica en absoluto. No se ejecuta ningún código adicional solo porque un método tiene un atributo, por lo que no hay dónde colocar su código TransactionScope
.
Lo que debería hacer es al inicio de la aplicación, usar la reflexión para iterar sobre cada método en cada clase en su ensamblaje y encontrar los métodos que están marcados con AtomicAttribute
, luego escribir un proxy personalizado alrededor de ese objeto. Luego, de alguna manera, haga que todo lo demás llame a su proxy en lugar de a la implementación real, quizás utilizando un marco de inyección de dependencias.
La mayoría de los marcos AOP hacen esto en tiempo de construcción. PostSharp, por ejemplo, se ejecuta después de que VisualStudio construya su ensamblaje. Escanea tu ensamblaje y vuelve a escribir el código IL para incluir los proxies y los interceptores AOP. De esta manera, el ensamblaje está listo para funcionar cuando se ejecuta, pero el IL ha cambiado con respecto a lo que escribiste originalmente.
Tal vez resolver todos los objetos utilizando el contenedor IoC? Puede configurar interceptores para sus tipos y en ellos verificar si el método llamado está decorado con ese atributo. Podría almacenar en caché esa información para no tener que usar la reflexión en cada llamada de método.
Así que cuando haces esto:
var something = IoC.Resolve<ISomething>();
something
no es objeto que has implementado sino proxy. En ese proxy puedes hacer lo que quieras antes y después de la llamada al método.