Domanda

Esiste un modo in C # o .NET in generale per creare un attributo su un metodo che innesca un evento quando viene invocato il metodo? Idealmente, sarei in grado di eseguire azioni personalizzate prima e dopo l'invocazione del metodo.

Intendo qualcosa del genere:

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

Non ho idea di come farlo o se possibile, ma System.Diagnostic.ConditionalAttribute potrebbe fare una cosa simile in background. Non sono sicuro però.

EDIT : ho dimenticato di menzionare che a causa delle circostanze del mio caso specifico, le prestazioni non sono realmente un problema.

È stato utile?

Soluzione

L'unico modo in cui so come farlo è con PostSharp . Post elabora il tuo IL e può fare cose come quello che hai chiesto.

Altri suggerimenti

Questo concetto viene utilizzato nelle MVC applicazioni web.

Il .NET Framework 4.x fornisce diversi attributi che attivano azioni, ad esempio: ExceptionFilterAttribute (gestione delle eccezioni), AuthorizeAttribute (gestione dell'autorizzazione ). Entrambi sono definiti in System.Web.Http.Filters .

Ad esempio, è possibile definire il proprio attributo di autorizzazione come segue:

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
    }

}

Quindi, nella tua classe controller decori i metodi che dovrebbero usare la tua autorizzazione come segue:

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

Ogni volta che viene invocato il metodo Post , chiamerà il metodo IsAuthorized all'interno dell'attributo myAuthorization prima viene eseguito il codice all'interno del metodo Post .

Se si restituisce false nel metodo IsAuthorized , si segnala che l'autorizzazione non viene concessa e l'esecuzione del metodo Post si interrompe.


Per capire come funziona, esaminiamo un esempio diverso: ExceptionFilter , che consente di filtrare le eccezioni utilizzando gli attributi, l'utilizzo è simile a quello mostrato sopra per AuthorizeAttribute (puoi trovare una descrizione più dettagliata del suo utilizzo qui ).

Per usarlo, ricava la classe DivideByZeroExceptionFilter dalla ExceptionFilterAttribute come mostrato qui e sovrascrive il metodo 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
                };
        }
    }
}

Quindi utilizzare il seguente codice demo per attivarlo:

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

Ora che sappiamo come viene utilizzato, siamo principalmente interessati all'implementazione. Il seguente codice proviene da .NET Framework. Utilizza l'interfaccia IExceptionFilter internamente come contratto:

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);
    }
}

Il ExceptionFilterAttribute stesso è definito come segue:

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();
        }
    }
}

All'interno di ExecuteExceptionFilterAsync , viene chiamato il metodo OnException . Poiché lo hai sovrascritto come mostrato in precedenza, ora l'errore può essere gestito con il tuo codice.


Esiste anche un prodotto commerciale disponibile come indicato nella risposta di OwenP, PostSharp , che ti consente di fare così facilmente. Qui è un esempio di come puoi farlo con PostSharp. Tieni presente che è disponibile un'edizione Express che puoi utilizzare gratuitamente anche per progetti commerciali.

Esempio PostSharp (vedi il link sopra per la descrizione completa):

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

Qui l'attributo specifica che il metodo Salva viene chiamato fino a 5 volte se si verifica un'eccezione. Il codice seguente definisce questo attributo personalizzato:

[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);
            }
        }
    }
}

Hai bisogno di una sorta di framework orientato all'aspetto. PostSharp lo farà, così come Windsor .

Fondamentalmente, eseguono la sottoclasse del tuo oggetto e sovrascrivono questo metodo ...

quindi diventa:

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

     _innerClass.DoSomeStuff();
}

ovviamente tutto ciò ti è nascosto. Tutto quello che devi fare è chiedere a Windsor il tipo e farà il proxy per te. L'attributo diventa una struttura (personalizzata) che penso in Windsor.

Puoi usare ContextBoundObject e IMessageSink. Vedi http://msdn.microsoft.com/nb- no / magazine / cc301356 (it-it) aspx

Tieni presente che questo approccio ha un forte impatto sulle prestazioni rispetto a una chiamata diretta al metodo.

Non credo che ci sia un modo per farlo con solo un attributo, ma usando classi proxy e riflessione potresti avere una classe che sa intercettare le istanze delle classi in cui hai attribuito metodi.

Quindi la classe proxy può attivare un evento ogni volta che vengono chiamati i metodi attribuiti.

Un attributo fornisce informazioni, sono metadati. Non conosco un modo per farlo con disinvoltura, qualcuno potrebbe.

È possibile esaminare metodi parziali in .NET che consentono di gestire in modo leggero gli eventi. Fornisci gli hook e consenti a qualcun altro di gestire l'implementazione. Se il metodo non è implementato, il compilatore lo ignora.

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

Potresti dare un'occhiata alla soluzione del povero: vedi il motivo del decoratore.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top