Para uma determinada classe, eu gostaria de ter a funcionalidade de rastreamento, ou seja, gostaria de registrar todas as chamadas de métodos (assinatura do método e valores reais dos parâmetros) e todas as saídas do método (apenas a assinatura do método).
Como faço para isso assumindo que:
- Não quero usar nenhuma biblioteca AOP de terceiros para C #,
- Não quero adicionar código duplicado a todos os métodos que desejo rastrear,
- Não quero alterar a API pública da classe - os usuários da classe devem poder chamar todos os métodos exatamente da mesma maneira.
Para tornar a questão mais concreta, vamos assumir que existem 3 classes:
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);
}
Como invoco Logger.LogStart e Logger.LogEnd para todas as chamadas para Method1 e Method2 sem modificar o método Caller.Call e sem adicionar as chamadas explicitamente a Traced.Method1 e Traced.Method2 ?
Edit: Qual seria a solução se eu tivesse permissão para alterar um pouco o método de chamada?
c#
reflection
aop
Journeyman
fonte
fonte
Respostas:
C # não é uma linguagem orientada para AOP. Possui alguns recursos de AOP e você pode emular outros, mas fazer AOP com C # é doloroso.
Procurei maneiras de fazer exatamente o que você queria fazer e não achei uma maneira fácil de fazê-lo.
Pelo que entendi, é isso que você deseja fazer:
e para fazer isso você tem duas opções principais
Herde sua classe de MarshalByRefObject ou ContextBoundObject e defina um atributo que herda de IMessageSink. Este artigo tem um bom exemplo. Você deve considerar, no entanto, que o uso de um MarshalByRefObject diminuirá como o inferno, e eu quero dizer, estou falando de um desempenho 10x perdido, portanto, pense com cuidado antes de tentar isso.
A outra opção é injetar código diretamente. Em tempo de execução, o que significa que você terá que usar a reflexão para "ler" todas as classes, obter seus atributos e injetar a chamada apropriada (e, nesse caso, acho que você não poderia usar o método Reflection.Emit como penso que Reflection.Emit não permite inserir um novo código dentro de um método já existente). Em tempo de design, isso significará a criação de uma extensão para o compilador CLR, que sinceramente não faço ideia de como isso é feito.
A opção final é usar uma estrutura de IoC . Talvez não seja a solução perfeita, pois a maioria das estruturas de IoC funciona definindo pontos de entrada que permitem que os métodos sejam conectados, mas, dependendo do que você deseja obter, essa pode ser uma aproximação justa.
fonte
Reflection.Emit
. Essa é a abordagem escolhida pelo Spring.NET . No entanto, isso exigiria métodos virtuais ativadosTraced
e não é realmente adequado para uso sem algum tipo de contêiner IOC, portanto, entendo por que essa opção não está na sua lista.A maneira mais simples de conseguir isso é provavelmente usando o PostSharp . Ele injeta código dentro de seus métodos com base nos atributos que você aplica a ele. Ele permite que você faça exatamente o que deseja.
Outra opção é usar a API de criação de perfil para injetar código dentro do método, mas isso é realmente sério.
fonte
Se você escrever uma classe - chamada Tracing - que implementa a interface IDisposable, você pode agrupar todos os corpos de métodos em um
Na classe Tracing, você pode lidar com a lógica dos rastreamentos no método construtor / Dispose, respectivamente, na classe Tracing para acompanhar a entrada e a saída dos métodos. De tal modo que:
fonte
Você poderia consegui-lo com o recurso de interceptação de um contêiner de DI, como o Castle Windsor . De fato, é possível configurar o contêiner de maneira que todas as classes que possuem um método decorado por um atributo específico sejam interceptadas.
Em relação ao ponto 3, o OP solicitou uma solução sem estrutura de AOP. Presumi na resposta a seguir que o que deveria ser evitado eram Aspect, JointPoint, PointCut etc. De acordo com a documentação da Interception da CastleWindsor , nenhum deles é necessário para realizar o que é solicitado.
Configure o registro genérico de um Interceptor, com base na presença de um atributo:
Adicione o IContributeComponentModelConstruction criado ao contêiner
E você pode fazer o que quiser no próprio interceptador
Adicione o atributo de log ao seu método para registrar
Observe que alguma manipulação do atributo será necessária se apenas algum método de uma classe precisar ser interceptado. Por padrão, todos os métodos públicos serão interceptados.
fonte
Se você deseja rastrear seus métodos sem limitação (sem adaptação de código, sem AOP Framework, sem código duplicado), deixe-me dizer, você precisa de alguma mágica ...
Sério, resolvi implementar um AOP Framework trabalhando em tempo de execução.
Você pode encontrar aqui: NConcern .NET AOP Framework
Decidi criar esse quadro de AOP para responder a esse tipo de necessidades. é uma biblioteca simples muito leve. Você pode ver um exemplo de logger na página inicial.
Se você não quiser usar um assembly de terceiros, poderá procurar a fonte de código (código aberto) e copiar os arquivos Aspect.Directory.cs e Aspect.Directory.Entry.cs para - conforme desejado. Essas classes permitem substituir seus métodos em tempo de execução. Eu apenas pediria que respeitasse a licença.
Espero que você encontre o que precisa ou o convença a finalmente usar um quadro de AOP.
fonte
Dê uma olhada nisso - coisas muito pesadas .. http://msdn.microsoft.com/en-us/magazine/cc164165.aspx
A caixa .net - don essencial tinha um capítulo sobre o que você precisa chamado Interceptação. Raspei algumas delas aqui (desculpe pelas cores da fonte - eu tinha um tema sombrio na época ...) http://madcoderspeak.blogspot.com/2005/09/essential-interception-using-contexts.html
fonte
Eu encontrei uma maneira diferente que pode ser mais fácil ...
Declarar um método InvokeMethod
Eu então defino meus métodos assim
Agora posso fazer a verificação em tempo de execução sem a injeção de dependência ...
Não há truques no site :)
Espero que você concorde que isso é menos pesado que um Framework AOP ou derivado de MarshalByRefObject ou usando classes de comunicação remota ou proxy.
fonte
Primeiro você precisa modificar sua classe para implementar uma interface (em vez de implementar o MarshalByRefObject).
Em seguida, você precisa de um objeto wrapper genérico baseado no RealProxy para decorar qualquer interface e permitir a interceptação de qualquer chamada para o objeto decorado.
Agora estamos prontos para interceptar chamadas para o Método1 e o Método2 do ITraced
fonte
Você pode usar a estrutura de código aberto CInject no CodePlex. Você pode escrever um código mínimo para criar um Injetor e interceptar qualquer código rapidamente com o CInject. Além disso, como esse é um código aberto, você também pode estender isso.
Ou você pode seguir as etapas mencionadas neste artigo em Interceptando chamadas de método usando IL e criar seu próprio interceptador usando as classes Reflection.Emit em C #.
fonte
Não conheço uma solução, mas minha abordagem seria a seguinte.
Decore a classe (ou seus métodos) com um atributo personalizado. Em outro lugar do programa, deixe uma função de inicialização refletir todos os tipos, leia os métodos decorados com os atributos e injete algum código IL no método. Na verdade, pode ser mais prático substituir o método por um esboço que chama
LogStart
, o método real e depoisLogEnd
. Além disso, não sei se você pode alterar os métodos usando a reflexão, para que seja mais prático substituir o tipo inteiro.fonte
Você pode usar o GOF Decorator Pattern e 'decorar' todas as classes que precisam de rastreamento.
Provavelmente é apenas realmente prático com um contêiner IOC (mas, como apontador anterior, você pode considerar a interceptação de método se quiser seguir o caminho do IOC).
fonte
você precisa incomodar Ayende para obter uma resposta sobre como ele fez isso: http://ayende.com/Blog/archive/2009/11/19/can-you-hack-this-out.aspx
fonte
AOP é uma obrigação para a implementação de código limpo; no entanto, se você deseja colocar um bloco em C #, métodos genéricos têm um uso relativamente mais fácil. (com senso intelectual e código fortemente tipado) Certamente, NÃO pode ser uma alternativa para AOP.
Embora o PostSHarp tenha poucos problemas com bugs (não me sinto confiante em usar na produção), é uma coisa boa.
Classe de wrapper genérico,
o uso pode ser assim (com senso de inteligência, é claro)
fonte
fonte
O melhor que você pode fazer antes do C # 6 com o 'nameof' lançado é usar as expressões StackTrace e linq lentas.
Por exemplo, para esse método
Essa linha pode ser produzida no seu arquivo de log
Aqui está a implementação:
fonte