Existe essa estrutura que estou ajudando a projetar. Existem algumas tarefas comuns que devem ser executadas usando alguns componentes comuns: Registrando, Armazenando em Cache e levantando eventos em particular.
Não tenho certeza se é melhor usar a injeção de dependência e apresentar todos esses componentes a cada serviço (como propriedades, por exemplo) ou devo colocar algum tipo de metadados sobre cada método dos meus serviços e usar interceptação para executar essas tarefas comuns ?
Aqui está um exemplo de ambos:
Injeção:
public class MyService
{
public ILoggingService Logger { get; set; }
public IEventBroker EventBroker { get; set; }
public ICacheService Cache { get; set; }
public void DoSomething()
{
Logger.Log(myMessage);
EventBroker.Publish<EventType>();
Cache.Add(myObject);
}
}
e aqui está a outra versão:
Interceptação:
public class MyService
{
[Log("My message")]
[PublishEvent(typeof(EventType))]
public void DoSomething()
{
}
}
Aqui estão as minhas perguntas:
- Qual solução é melhor para uma estrutura complicada?
- Se a interceptação vencer, quais são minhas opções para interagir com os valores internos de um método (para usar com o serviço de cache, por exemplo?)? Posso usar outras maneiras, em vez de atributos, para implementar esse comportamento?
- Ou talvez haja outras soluções para resolver o problema?
c#
dependency-injection
Beatles1692
fonte
fonte
Respostas:
Preocupações transversais como registro em log, armazenamento em cache etc. não são dependências, portanto não devem ser injetadas nos serviços. No entanto, embora a maioria das pessoas pareça alcançar uma estrutura de AOP intercalada completa, há um bom padrão de design para isso: Decorator .
No exemplo acima, deixe o MyService implementar a interface IMyService:
Isso mantém a classe MyService completamente livre de preocupações transversais, seguindo o Princípio de responsabilidade única (SRP).
Para aplicar o log, você pode adicionar um Decorator de log:
Você pode implementar armazenamento em cache, medição, eventos, etc. da mesma maneira. Cada decorador faz exatamente uma coisa, então eles também seguem o SRP e você pode compor de maneiras arbitrariamente complexas. Por exemplo
fonte
Para alguns serviços, acho que a resposta de Mark é boa: você não precisará aprender ou introduzir novas dependências de terceiros e ainda seguirá os bons princípios do SOLID.
Para uma grande quantidade de serviços, eu recomendaria uma ferramenta de AOP como PostSharp ou Castle DynamicProxy. O PostSharp possui uma versão gratuita (como na cerveja) e eles lançaram recentemente o PostSharp Toolkit for Diagnostics (gratuito como na cerveja E fala), que fornecerá alguns recursos de registro prontos para uso.
fonte
Acho que o design de uma estrutura é amplamente ortogonal a essa pergunta - você deve se concentrar primeiro na interface da sua estrutura e, talvez, como processo mental de fundo, considere como alguém pode realmente consumi-la. Você não deseja fazer algo que impeça que seja usado de maneira inteligente, mas deve ser apenas uma entrada para o design da estrutura; um entre muitos.
fonte
Já enfrentei esse problema muitas vezes e acho que encontrei uma solução simples.
Inicialmente, segui o padrão decorador e implementei manualmente cada método, quando você tem centenas de métodos, isso se torna muito entediante.
Decidi então usar o PostSharp, mas não gostei da ideia de incluir uma biblioteca inteira apenas para fazer algo que eu pudesse realizar com (muito) código simples.
Depois, segui a rota do proxy transparente que era divertida, mas envolvia a emissão dinâmica de IL em tempo de execução e não seria algo que eu gostaria de fazer em um ambiente de produção.
Recentemente, decidi usar modelos T4 para implementar automaticamente o padrão do decorador em tempo de design. Acontece que os modelos T4 são realmente muito difíceis de trabalhar e eu precisava disso rapidamente, então criei o código abaixo. É rápido e sujo (e não suporta propriedades), mas espero que alguém o ache útil.
Aqui está o código:
Aqui está um exemplo:
Em seguida, crie uma classe chamada LoggingTestAdapter que implemente o ITestAdapter, faça com que o visual studio implemente automaticamente todos os métodos e execute-o pelo código acima. Você deve ter algo parecido com isto:
É isso com o código de suporte:
fonte