Pregunta

¿Hay alguna forma en C # o .NET en general para crear un atributo en un método que desencadena un evento cuando se invoca el método? Idealmente, podría ejecutar acciones personalizadas antes y después de la invocación del método.

Me refiero a algo como esto:

[TriggersMyCustomAction()]
public void DoSomeStuff()
{
}

No tengo ni idea de cómo hacerlo o si es posible, pero System.Diagnostic.ConditionalAttribute podría hacer algo similar en segundo plano. Sin embargo, no estoy seguro.

EDITAR : olvidé mencionar que debido a las circunstancias de mi caso específico, el rendimiento no es realmente un problema.

¿Fue útil?

Solución

La única forma en que sé cómo hacer esto es con PostSharp . Postprocesa su IL y puede hacer cosas como lo que solicitó.

Otros consejos

Este concepto se utiliza en MVC aplicaciones web.

.NET Framework 4.x proporciona varios atributos que desencadenan acciones, por ejemplo: ExceptionFilterAttribute (manejo de excepciones), AuthorizeAttribute (manejo de autorización ) Ambos se definen en System.Web.Http.Filters .

Podría, por ejemplo, definir su propio atributo de autorización de la siguiente manera:

public class myAuthorizationAttribute : AuthorizeAttribute
{
    protected override bool IsAuthorized(HttpActionContext actionContext)
    {
        // do any stuff here
        // it will be invoked when the decorated method is called
        if (CheckAuthorization(actionContext)) 
           return true; // authorized
        else
           return false; // not authorized
    }

}

Luego, en su clase de controlador decora los métodos que se supone que usan su autorización de la siguiente manera:

[myAuthorization]
public HttpResponseMessage Post(string id)
{
    // ... your code goes here
    response = new HttpResponseMessage(HttpStatusCode.OK); // return OK status
    return response;
}

Cada vez que se invoca el método Post , llamará al método IsAuthorized dentro del atributo myAuthorization antes se ejecuta el código dentro del método Post .

Si devuelve false en el método IsAuthorized , indica que no se concede la autorización y se cancela la ejecución del método Post .


Para comprender cómo funciona esto, veamos un ejemplo diferente: El ExceptionFilter , que permite filtrar excepciones mediante el uso de atributos, el uso es similar al que se muestra arriba para AuthorizeAttribute (puede encontrar una descripción más detallada sobre su uso aquí ).

Para usarlo, obtenga la clase DivideByZeroExceptionFilter de la ExceptionFilterAttribute como se muestra aquí , y anule el método OnException :

public class DivideByZeroExceptionFilter : ExceptionFilterAttribute
{
    public override void OnException(HttpActionExecutedContext actionExecutedContext)
    {
        if (actionExecutedContext.Exception is DivideByZeroException)
        {
            actionExecutedContext.Response = new HttpResponseMessage() { 
                Content = new StringContent("An error occured within the application.",
                                System.Text.Encoding.UTF8, "text/plain"), 
                StatusCode = System.Net.HttpStatusCode.InternalServerError
                };
        }
    }
}

Luego use el siguiente código de demostración para activarlo:

[DivideByZeroExceptionFilter]
public void Delete(int id)
{
    // causes the DivideByZeroExceptionFilter attribute to be triggered:
    throw new DivideByZeroException(); 
}

Ahora que sabemos cómo se usa, estamos interesados ??principalmente en la implementación. El siguiente código es de .NET Framework. Utiliza la interfaz IExceptionFilter internamente como un contrato:

namespace System.Web.Http.Filters
{
    public interface IExceptionFilter : IFilter
    {
        // Executes an asynchronous exception filter.
        // Returns: An asynchronous exception filter.
        Task ExecuteExceptionFilterAsync(
                    HttpActionExecutedContext actionExecutedContext, 
                    CancellationToken cancellationToken);
    }
}

El ExceptionFilterAttribute se define de la siguiente manera:

namespace System.Web.Http.Filters
{
    // Represents the attributes for the exception filter.
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, 
            Inherited = true, AllowMultiple = true)]
    public abstract class ExceptionFilterAttribute : FilterAttribute, 
            IExceptionFilter, IFilter
    {
        // Raises the exception event.
        // actionExecutedContext: The context for the action.</param>
        public virtual void OnException(
            HttpActionExecutedContext actionExecutedContext)
        {
        }
        // Asynchronously executes the exception filter.
        // Returns: The result of the execution.
        Task IExceptionFilter.ExecuteExceptionFilterAsync(
            HttpActionExecutedContext actionExecutedContext, 
            CancellationToken cancellationToken)
        {
            if (actionExecutedContext == null)
            {
                throw Error.ArgumentNull("actionExecutedContext");
            }
            this.OnException(actionExecutedContext);
            return TaskHelpers.Completed();
        }
    }
}

Dentro de ExecuteExceptionFilterAsync , se llama al método OnException . Debido a que lo ha anulado como se muestra anteriormente, el error ahora puede ser manejado por su propio código.


También hay un producto comercial disponible como se menciona en la respuesta de OwenP, PostSharp , que le permite hacer que fácilmente Aquí es un ejemplo de cómo puede hacerlo con PostSharp. Tenga en cuenta que hay una edición Express disponible que puede usar de forma gratuita incluso para proyectos comerciales.

Ejemplo de PostSharp (consulte el enlace de arriba para obtener una descripción completa):

public class CustomerService
{
    [RetryOnException(MaxRetries = 5)]
    public void Save(Customer customer)
    {
        // Database or web-service call.
    }
}

Aquí el atributo especifica que el método Guardar se llama hasta 5 veces si se produce una excepción. El siguiente código define este atributo personalizado:

[PSerializable]
public class RetryOnExceptionAttribute : MethodInterceptionAspect
{
    public RetryOnExceptionAttribute()
    {
        this.MaxRetries = 3;
    }

    public int MaxRetries { get; set; }

    public override void OnInvoke(MethodInterceptionArgs args)
    {
        int retriesCounter = 0;

        while (true)
        {
            try
            {
                args.Proceed();
                return;
            }
            catch (Exception e)
            {
                retriesCounter++;
                if (retriesCounter > this.MaxRetries) throw;

                Console.WriteLine(
                    "Exception during attempt {0} of calling method {1}.{2}: {3}",
                    retriesCounter, args.Method.DeclaringType, args.Method.Name, e.Message);
            }
        }
    }
}

Necesita algún tipo de marco orientado a aspectos. PostSharp lo hará, al igual que Windsor .

Básicamente, subclasifican su objeto y anulan este método ...

luego se convierte en:

//proxy
public override void DoSomeStuff()
{
     if(MethodHasTriggerAttribute)
        Trigger();

     _innerClass.DoSomeStuff();
}

por supuesto, todo esto está oculto para ti. Todo lo que tiene que hacer es pedirle a Windsor el tipo, y lo hará por usted. El atributo se convierte en una instalación (personalizada), creo en Windsor.

Puede usar ContextBoundObject e IMessageSink. Consulte http://msdn.microsoft.com/nb- no / magazine / cc301356 (es-es) .aspx

Tenga en cuenta que este enfoque tiene un grave impacto en el rendimiento en comparación con una llamada de método directo.

No creo que haya una manera de hacerlo con solo un atributo, pero usando clases proxy y reflexión, podría tener una clase que sepa interceptar instancias de las clases en las que ha atribuido métodos.

Entonces la clase proxy puede desencadenar un evento siempre que se invoquen los métodos atribuidos.

Un atributo proporciona información, son metadatos. No sé de una manera de hacer esto de manera espontánea, alguien podría hacerlo.

Podrías mirar métodos parciales en .NET que te permiten hacer un manejo ligero de eventos. Usted proporciona los ganchos y deja que otra persona maneje la implementación. Si el método no se implementa, el compilador simplemente lo ignora.

http://msdn.microsoft.com/en-us/library /wa80x488.aspx

Puede echar un vistazo a la solución del pobre hombre: vea el patrón decorador.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top