2009-12-21 10 views
10

tengo la siguiente interfaz:Usando StructureMap con la envoltura log4net

public interface ILogger 
{ 
    void Debug(string message, params object[] values); 
    void Info(string message, params object[] values); 
    void Warn(string message, params object[] values); 
    void Error(string message, params object[] values); 
    void Fatal(string message, params object[] values); 
} 

y la siguiente implementación:

public class Log4netLogger : ILogger 
{ 
    private ILog _log; 

    public Log4netLogger(Type type) 
    { 
     _log = LogManager.GetLogger(type); 
    } 

    public void Debug(string message, params object[] values) 
    { 
     _log.DebugFormat(message, values); 
    } 

    // other logging methods here... 

} 

Mi idea era utilizar StructureMap para crear instancias de la clase Log4netLogger con el uso del tipo de la clase que hizo la tala Sin embargo, no puedo entender cómo pasar el tipo de la clase que llama a structuremap para que pueda pasarse al constructor de la implementación del registro. Cualquier consejo sobre cómo hacer eso (o una mejor manera) sería muy apreciado.

Respuesta

1

Si el parámetro de tipo es específico del contexto, no creo que esto funcione como se muestra. Si tiene que pasar algo contexto específico en el constructor, es probable que va a tener que crear una interfaz de fábrica y aplicación que devuelve una instancia de la ILogger:

public interface ILoggerFactory 
{ 
    ILogger Create(Type type); 
} 

public class LoggerFactory : ILoggerFactory 
{ 
    public ILogger Create(Type type) 
    { 
     return new Log4netLogger(type); 
    } 
} 

podría ser posible para arrancar StructureMap para abastecer el instancia que desee según el tipo, pero eso supone un número limitado de tipos que conoce de antemano.

+1

La necesidad de fábricas cuando se usa structuremap generalmente se puede eliminar utilizando un "ConstrutedBy" con una lambda como método de fábrica. http://structuremap.sourceforge.net/InstanceExpression.htm#section13 – KevM

1

Realmente necesito dejar el hábito de responder mi propia pregunta, pero para aquellos que se encuentran con ella, esta es la respuesta.

return ObjectFactory.With(type).GetInstance<T>(); 

en realidad tienen un envoltorio a StructureMap (para evitar la exposición de la dependencia StructureMap a mi aplicación) que se parece a lo siguiente:

public static class ServiceManager 
{ 
    public static T Get<T>() 
    { 
     return ObjectFactory.GetInstance<T>(); 
    } 

    public static T Get<T>(Type type) 
    { 
     return ObjectFactory.With(type).GetInstance<T>(); 
    } 
} 

cualquier momento en el código que necesito un registrador, me llaman el siguiente:

ServiceManager.Get<ILogger>(GetType()).Info("Logging page view..."); 
+1

De hecho, no creo que haya nada de malo en considerar el registrador como un servicio global (ya que generalmente es transversal), pero esto crea una dependencia estática en su ServiceManager. No digo que su solución sea incorrecta (ni mucho menos), pero suena un poco a que tiende hacia el localizador de servicios globales "antipatrón". Esencialmente, su dependencia de ILogger (o ILoggerFactory) es (o puede ser) ya no más explícita. –

+0

Punto interesante Phil. No estoy seguro de cuál sería la alternativa (las recomendaciones arquitectónicas sobre IoC son escasas, de hecho). En mi caso, la clase es un HttpHandler que necesita acceso al ILogger que está solicitando desde el ServiceManager. ¿Cómo más lo implementaría? – Chris

+0

Estoy absolutamente de acuerdo en que las recomendaciones de arquitectura son escasas, y su pregunta en mi humilde opinión es * la * obstáculo más común que las personas encuentran cuando están comenzando con un marco DI. Crear una fábrica abstracta es la mejor respuesta que he encontrado, y a menudo implemento la fábrica usando StructureMap (ObjectFactory.GetInstance) para que las dependencias que están más abajo en la cadena todavía puedan ser manejadas por contenedor. Esto puede no ser * perfecto *, pero al menos las dependencias se mantienen explícitas. –

23

Utilizamos un envoltorio de ILogger similar alrededor de log4net y normalmente usamos la inyección de constructor. Usamos un interceptor como método de fábrica responsable de crear el registrador. Aquí está nuestro registro típico para la configuración de registro.

public class CommonsRegistry : Registry 
{ 
    public CommonsRegistry() 
    { 
     For<ILogger>() 
      .AlwaysUnique() 
      .TheDefault.Is.ConstructedBy(s => 
      { 
       if (s.ParentType == null) 
        return new Log4NetLogger(s.BuildStack.Current.ConcreteType); 

       return new Log4NetLogger(s.ParentType); 
      }); 

     var applicationPath = Path.GetDirectoryName(Assembly.GetAssembly(GetType()).Location); 
     var configFile = new FileInfo(Path.Combine(applicationPath, "log4net.config")); 
     XmlConfigurator.ConfigureAndWatch(configFile); 
    } 
} 

La verificación nula del tipo primario es necesaria cuando hay dependencias en tipos de hormigón.

El resto es cosas opcionales de configuración de log4net.

Una cosa que me gusta de esta configuración es la capacidad de utilizar un registrador nulo para probar la unidad.

+0

Interesante, tendré que probar esto. ¡Gracias! – Chris

+0

Esto es realmente interesante. También vi tu comentario en mi respuesta. ¿Esto funcionaría si la dependencia de ctor fuera algo fuera del tipo de la clase llamante? Me encantaría ver una publicación de blog sobre dependencias de ctor específicas del contexto, ya que esta debe ser la pregunta número 1 más frecuente sobre el uso de un marco DI. –

+0

Además, estoy luchando con si esta solución hace que la dependencia sea menos explícita. Si la construcción realmente requiere una fábrica, ¿es una buena idea "esconder" la fábrica en el código de configuración, en lugar de hacer explícita la dependencia de una fábrica abstracta? (Me refiero a esto como una pregunta teórica, no necesariamente como la mejor solución para Chris o para iniciar sesión per se). –

Cuestiones relacionadas