Pergunta

Existe uma maneira em C # ou .NET em geral para criar um atributo em um método que desencadeia um evento quando o método é chamado? Idealmente, eu seria capaz de executar ações personalizadas antes e depois da invocação do método.

Eu quero dizer algo como isto:

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

Eu sou totalmente ignorante como fazê-lo ou se possível a todos, mas System.Diagnostic.ConditionalAttribute pode fazer uma coisa semelhante em segundo plano. Eu não tenho certeza embora.

Editar :. Eu esqueci de mencionar que, devido às circunstâncias de meu caso específico, o desempenho não é realmente um problema

Foi útil?

Solução

A única maneira que eu sei como fazer isso é com PostSharp . É pós-processa o seu IL e pode fazer coisas como o que você pediu.

Outras dicas

Este conceito é usado na MVC aplicações web.

O .NET Framework 4.x fornece vários atributos que ações de disparo, por exemplo .: ExceptionFilterAttribute (tratamento de exceções), AuthorizeAttribute (autorização manipulação). Ambos são definidos no System.Web.Http.Filters.

Você poderia, por exemplo, definir o seu próprio atributo autorização da seguinte forma:

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
    }

}

Então, em sua controller classe que você decorar os métodos que deveriam usar a sua autorização da seguinte forma:

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

Sempre que o método Post é invocado, ele irá chamar o método IsAuthorized dentro do Atributo myAuthorization antes o código dentro do método Post é executado.

Se você retornar false no método IsAuthorized, você sinalizar que a autorização não é concedida e a execução dos aborts método Post.


Para entender como isso funciona, vamos olhar para um exemplo diferente: O ExceptionFilter , que permite filtrar exceções usando atributos, o uso é semelhante ao mostrado acima para o AuthorizeAttribute (você pode encontrar um mais descrição detalhada sobre a sua utilização aqui ).

Para usá-lo, derivar a classe DivideByZeroExceptionFilter do ExceptionFilterAttribute como mostrado aqui , e substituir o OnException método:

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

Em seguida, use o seguinte código de demonstração para provocá-lo:

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

Agora que sabemos como ela é usada, estamos interessados ??principalmente na implementação. O código a seguir é do .NET Framework. Ele usa o IExceptionFilter de interface internamente como um 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);
    }
}

O ExceptionFilterAttribute em si é definido da seguinte forma:

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 ExecuteExceptionFilterAsync, o OnException método é chamado. Porque você tem substituído-o como mostrado anteriormente, o erro pode agora ser tratado pelo seu próprio código.


Há também um produto comercial disponível como mencionado na resposta do OwenP, PostSharp , que permite que você faça que facilmente. Aqui é um exemplo de como você pode fazer isso com PostSharp. Note-se que há uma edição Express disponíveis, que você pode usar para livre, mesmo para projetos comerciais.

PostSharp Exemplo (ver a ligação acima para a descrição completa):

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

Aqui especifica atributos que o método Save é chamado até 5 vezes se ocorrer uma exceção. O código a seguir 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);
            }
        }
    }
}

Você precisa de algum tipo de estrutura orientada Aspect. PostSharp vai fazê-lo, assim como Windsor .

Basicamente, eles subclasse seu objeto e substituir esse método ...

então torna-se:

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

     _innerClass.DoSomeStuff();
}

Claro que tudo isso está oculto para você. Tudo que você tem a fazer é pedir Windsor para o tipo, e ele vai fazer o proxy para você. O atributo torna-se um (personalizado) facilidade Eu acho que em Windsor.

Você pode usar ContextBoundObject e IMessageSink. Consulte http://msdn.microsoft.com/nb- não / revista / cc301356 (en-us) .aspx

Esteja avisado que esta abordagem tem um impacto no desempenho grave, em comparação com uma chamada de método direto.

Eu não acho que há uma maneira de fazê-lo com apenas um atributo, mas usando classes de proxy e reflexão você poderia ter uma classe que sabe instantiations interceptar das classes em que você tem atribuídas métodos.

Em seguida, a classe de proxy pode desencadear um evento sempre atribuídos os métodos são chamados.

Um atributo dá informações, eles são metadados. Eu não sei de uma maneira de fazer isso sem constrangimento, alguém pode.

Você pode olhar para métodos parciais em .NET que lhe permitem fazer alguma manipulação de eventos leve. Você fornece os ganchos e deixar alguém lidar com a implementação. Se o método não é implementado o compilador simplesmente ignora-lo.

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

Você pode dar uma olhada na solução do homem pobre:. Ver o padrão decorador

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top