2009-12-21 12 views
10

Mam następujący interfejs:Korzystanie StructureMap z log4net owijki

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

i następujący realizacji:

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

} 

Mój pomysł polegał na użyciu StructureMap instancję klasy Log4netLogger z użyciem rodzaju klasa, która zrobiła logowanie. Jednak nie mogę dla mojego życia dowiedzieć się, jak przekazać typ wywołującej klasy do structuremap, tak aby mogła zostać przekazana do konstruktora implementacji rejestrowania. Jakakolwiek rada, jak to zrobić (lub lepszy sposób) byłaby najbardziej doceniana.

Odpowiedz

1

Jeśli parametr typu jest specyficzny dla kontekstu, nie sądzę, że to zadziała, jak pokazano. Jeśli trzeba przekazać kontekst coś konkretnego w konstruktorze, jesteś prawdopodobnie będzie musiał utworzyć interfejs fabrycznego i realizację, która zwraca instancję ILogger:

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

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

to może być możliwe, aby bootstrap StructureMap dostarczyć instancja, którą chcesz na podstawie typu, ale która zakłada ograniczoną liczbę typów, które znasz z góry.

+1

Potrzeba dla fabryk podczas korzystania z mapy struktury zwykle można wyeliminować za pomocą metody "ConstructedBy" z lambda jako metodą fabryczną. http://structuremap.sourceforge.net/InstanceExpression.htm#section13 – KevM

1

Naprawdę muszę wyjść z przyzwyczajenia odpowiadania na moje własne pytanie, ale dla tych, którzy go przeżywają, oto odpowiedź.

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

I rzeczywiście mają owijkę do StructureMap (aby uniknąć narażania zależność StructureMap do mojego app), który wygląda następująco:

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

każdym razem w kodzie muszę rejestrator wzywam następujące:

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

Nie sądzę, że jest coś złego w tym, że logger jest usługą globalną (ponieważ generalnie jest to przekrojowa), ale powoduje to statyczną zależność na twoim ServiceManager. Nie mówię, że twoje rozwiązanie jest złe (z dala od niego), ale brzmi trochę tak, jakby mogło zmierzać w kierunku globalnego lokalizatora usług "anty-wzór". Zasadniczo twoja zależność od ILogger (lub ILoggerFactory) jest (lub może być) już jawna. –

+0

Interesujący punkt Phil. Nie jestem pewna, jaka byłaby alternatywa (zalecenia architektoniczne dotyczące IoC są rzeczywiście skąpe). W moim przypadku klasa jest HttpHandler, który potrzebuje dostępu do ILogger, którego żąda od ServiceManager. Jak inaczej mógłbym wprowadzić? – Chris

+0

Absolutnie zgadzam się, że zalecenia dotyczące architektury są rzadkie, a twoje pytanie: IMHO jest * najczęstszą przeszkodą, z którą spotykają się ludzie, gdy zaczynają pracę z systemem DI. Tworzenie abstrakcyjnej fabryki to najlepsza odpowiedź, jaką wymyśliłem, i często wdrażam fabrykę za pomocą StructureMap (ObjectFactory.GetInstance), aby zależności dalej w dół łańcucha mogły być nadal zarządzane przez kontener. To może nie być * idealne *, ale przynajmniej zależności są jawne. –

23

Używamy podobnego opakowania ILogger wokół log4net i zwykle używamy wtrysku konstruktora. Używamy przechwytywacza jako metody fabrycznej odpowiedzialnej za tworzenie rejestratora. Oto nasz typowy rejestr do konfiguracji rejestrowania.

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

Kontrola typu zerowego typu jest konieczna, gdy istnieją zależności od konkretnych typów.

Reszta to opcjonalne elementy konfiguracji protokołu log4net.

Jedną rzeczą, którą lubię w tej konfiguracji, jest możliwość korzystania z zerowych rejestratorów do testowania jednostkowego.

+0

Interesujące, będę musiał spróbować. Dzięki! – Chris

+0

To naprawdę interesujące. Widziałem również twój komentarz do mojej odpowiedzi. Czy to działałoby, gdyby zależność ctor była czymś poza typem klasy wywołującej? Chciałbym zobaczyć post na blogu o zależnościach zależnych od kontekstu, ponieważ jest to najczęściej zadawane pytanie # 1 dotyczące korzystania z frameworka DI. –

+0

Ponadto, mam problem z tym, czy to rozwiązanie sprawia, że ​​zależność jest mniej wyraźna. Jeśli budowa naprawdę wymaga fabryki, czy dobrze jest "ukryć" fabrykę w kodzie konfiguracyjnym, zamiast uzależniać ją od abstrakcyjnej fabryki? (Mam na myśli to jako teoretyczne pytanie, niekoniecznie jako najlepsze rozwiązanie dla Chrisa lub logowania jako takie). –