Existe este marco que estoy ayudando a diseñar. Hay algunas tareas comunes que deben realizarse utilizando algunos componentes comunes: registro, almacenamiento en caché y eventos de elevación en particular.
No estoy seguro de si es mejor usar la inyección de dependencia e introducir todos estos componentes en cada servicio (como propiedades, por ejemplo) o si debería colocar algún tipo de metadatos sobre cada método de mis servicios y utilizar la intercepción para realizar estas tareas comunes ?
Aquí hay un ejemplo de ambos:
Inyección:
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);
}
}
y aquí está la otra versión:
Interceptación:
public class MyService
{
[Log("My message")]
[PublishEvent(typeof(EventType))]
public void DoSomething()
{
}
}
Aquí están mis preguntas:
- ¿Qué solución es mejor para un marco complicado?
- Si la intercepción gana, ¿cuáles son mis opciones para interactuar con los valores internos de un método (para usar con el servicio de caché, por ejemplo)? ¿Puedo usar otras formas en lugar de atributos para implementar este comportamiento?
- ¿O tal vez puede haber otras soluciones para resolver el problema?
c#
dependency-injection
Beatles1692
fuente
fuente
Respuestas:
Las preocupaciones transversales como el registro, el almacenamiento en caché, etc. no son dependencias, por lo que no deben inyectarse en los servicios. Sin embargo, aunque la mayoría de las personas parecen alcanzar un marco de AOP entrelazado completo, hay un buen patrón de diseño para esto: Decorador .
En el ejemplo anterior, deje que MyService implemente la interfaz IMyService:
Esto mantiene a la clase MyService completamente libre de preocupaciones transversales, siguiendo así el Principio de responsabilidad única (SRP).
Para aplicar el registro, puede agregar un Decorador de registro:
Puede implementar el almacenamiento en caché, la medición, los eventos, etc. de la misma manera. Cada decorador hace exactamente una cosa, por lo que también siguen el SRP, y puede componerlos de formas arbitrariamente complejas. P.ej
fuente
Para un puñado de servicios, creo que la respuesta de Mark es buena: no tendrá que aprender o introducir nuevas dependencias de terceros y aún seguirá buenos principios SÓLIDOS.
Para una gran cantidad de servicios, recomendaría una herramienta AOP como PostSharp o Castle DynamicProxy. PostSharp tiene una versión gratuita (como en cerveza), y recientemente lanzaron PostSharp Toolkit for Diagnostics , (gratis como en cerveza Y discurso) que le dará algunas características de registro listas para usar .
fuente
Creo que el diseño de un marco es en gran parte ortogonal a esta pregunta: primero debe centrarse en la interfaz de su marco y tal vez como un proceso mental de fondo considere cómo alguien realmente podría consumirlo. No desea hacer algo que evite que se use de manera inteligente, pero solo debe ser una entrada en su diseño de marco; uno entre muchos
fuente
Me he enfrentado a este problema muchas veces y creo que se me ocurrió una solución simple.
Inicialmente, utilicé el patrón de decorador e implementé manualmente cada método, cuando tienes cientos de métodos, esto se vuelve muy tedioso.
Luego decidí usar PostSharp, pero no me gustó la idea de incluir una biblioteca completa solo para hacer algo que pudiera lograr con (una gran cantidad) de código simple.
Luego seguí la ruta del proxy transparente, que era divertido pero implicaba emitir dinámicamente IL en tiempo de ejecución y no sería algo que quisiera hacer en un entorno de producción.
Recientemente decidí usar plantillas T4 para implementar automáticamente el patrón de decorador en el momento del diseño, resulta que las plantillas T4 son bastante difíciles de trabajar y necesitaba hacer esto rápidamente, así que creé el siguiente código. Es rápido y sucio (y no admite propiedades), pero espero que alguien lo encuentre útil.
Aquí está el código:
Aquí hay un ejemplo:
Luego cree una clase llamada LoggingTestAdapter que implemente ITestAdapter, obtenga un estudio visual para implementar automáticamente todos los métodos y luego ejecútelo a través del código anterior. Entonces deberías tener algo como esto:
Esto es con el código de soporte:
fuente