2009-12-21 15 views
10

Ho la seguente interfaccia:Utilizzando StructureMap con log4net involucro

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

e la seguente implementazione:

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... 

} 

La mia idea era quella di utilizzare StructureMap creare un'istanza della classe Log4netLogger con l'utilizzo del tipo della classe che ha fatto il logging. Tuttavia, non posso per la vita di me capire come passare il tipo della classe chiamante a structuremap in modo che possa essere passato al costruttore dell'implementazione della registrazione. Qualunque consiglio su come farlo (o un modo migliore) sarebbe più apprezzato.

risposta

1

Se il parametro type è specifico del contesto, non penso che funzionerà come mostrato. Se è necessario passare qualcosa di specifico contesto nel costruttore, si sta probabilmente andando ad avere per creare un'interfaccia fabbrica e implementazione che restituisce un'istanza della ILogger:

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

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

potrebbe essere possibile il bootstrap StructureMap per fornire il istanza che vuoi in base al tipo, ma che presuppone un numero limitato di tipi che conosci in anticipo.

+1

L'esigenza di utilizzare le fabbriche quando si utilizza la structuremap può essere generalmente eliminata utilizzando un "ConstructedBy" con un lambda come metodo di fabbrica. http://structuremap.sourceforge.net/InstanceExpression.htm#section13 – KevM

1

Ho davvero bisogno di uscire dall'abitudine di rispondere alla mia domanda, ma per quelli che ci sono davanti, ecco la risposta.

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

Mi hanno fatto un wrapper per StructureMap (per evitare di esporre la dipendenza StructureMap alla mia app) che è simile al seguente:

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

Ogni volta che nel codice ho bisogno di un registratore, mi chiamano il seguente:

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

In realtà non penso ci sia nulla di sbagliato nel considerare il logger un servizio globale (dato che generalmente è trasversale), ma questo crea una dipendenza statica sul tuo ServiceManager. Non sto dicendo che la tua soluzione sia sbagliata (tutt'altro), ma suona un po 'come potrebbe tendere verso l'anti-pattern di localizzazione globale del servizio. Essenzialmente la tua dipendenza da ILogger (o ILoggerFactory) è (o potrebbe essere) non più esplicita. –

+0

Punto interessante Phil. Non sono sicuro di quale sarebbe l'alternativa (le raccomandazioni architetturali su IoC sono scarse, in effetti). Nel mio caso, la classe è un HttpHandler che ha bisogno di accedere a ILogger che sta richiedendo dal ServiceManager. In quale altro modo dovrei implementare? – Chris

+0

Sono assolutamente d'accordo sul fatto che le raccomandazioni di architettura siano scarse e la tua domanda IMHO è * il * ostacolo più comune che le persone incontrano quando iniziano con un framework DI. Creare una fabbrica astratta è la migliore risposta che mi sia venuta in mente, e spesso realizzo la fabbrica usando StructureMap (ObjectFactory.GetInstance) in modo che le dipendenze in fondo alla catena possano ancora essere gestite dal contenitore. Questo potrebbe non essere * perfetto *, ma almeno le dipendenze sono mantenute esplicite. –

23

Utilizziamo un wrapper di ILogger simile su log4net e in genere si utilizza l'iniezione del costruttore. Usiamo un intercettatore come metodo factory responsabile della creazione del Logger. Ecco il nostro tipico registro per l'impostazione della registrazione.

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

Il controllo del tipo genitore null è necessario quando ci sono dipendenze su tipi di calcestruzzo.

Il resto è materiale di installazione log4net opzionale.

Una cosa che mi piace di questa configurazione è la possibilità di utilizzare un logger null per il test dell'unità.

+0

Interessante, dovrò provare questo. Grazie! – Chris

+0

Questo è davvero interessante. Ho visto anche il tuo commento sulla mia risposta. Funzionerebbe se la dipendenza del ctor fosse qualcosa al di fuori del tipo della classe chiamante? Mi piacerebbe vedere un post sul blog su dipendenze di ctor specifici del contesto, in quanto questa deve essere la domanda più frequente # 1 sull'utilizzo di un framework DI. –

+0

Inoltre, sto lottando con se questa soluzione rende la dipendenza meno esplicita. Se la costruzione richiede realmente una fabbrica, è una buona idea "nascondere" la fabbrica nel codice di configurazione, invece di rendere esplicita la dipendenza da una fabbrica astratta? (Intendo questo come una domanda teorica, non necessariamente come la soluzione migliore per Chris o per la registrazione di per sé). –