0

Following this answer I did this :

public class Log4netAdapter<T> : ILogger
{
    private static readonly log4net.ILog logger = LogManager.GetLogger(typeof(T));

    public void Log(LogEntry entry)
    {
        if(entry.LoggingEventType == LoggingEventType.Information)
            logger.Info(entry.Message, entry.Exception);
        else if(entry.LoggingEventType == LoggingEventType.Warning)
            logger.Warn(entry.Message, entry.Exception);
        else if(entry.LoggingEventType == LoggingEventType.Error)
            logger.Error(entry.Message, entry.Exception);
        else
            logger.Fatal(entry.Message, entry.Exception);
    }
}

And then on the Simple Injector :

container.RegisterConditional(
    typeof(ILogger),
    c => typeof(Log4netAdapter<>).MakeGenericType(c.Consumer.ImplementationType),
    Lifestyle.Singleton,
    c => true);

This works great if I inject the ILogger on every class constructor I need. My problem is that I have some classes that I cannot use the constructor injection. For this cases I would tipically do :

var logger = SimpleInjectorInitializer.Container.GetInstance<ILogger>();

However the above method does not work, it throws an error on the simple injector class since the c.Consumer is null.

Here is one of the examples I need to resolve ILogger, this class is registered on the webapi startup class.

public class ExceptionWebApiHandlingAttribute : IExceptionFilter
{
    public ExceptionWebApiHandlingAttribute()
    {
    }   
}

Is there any alternative ?

Thanks

Community
  • 1
  • 1
user2779312
  • 661
  • 2
  • 9
  • 23

1 Answers1

3

When working on the application boundary, it is sometimes hard or impossible to use constructor injection. Typical examples are MVC filter attributes or ASP.NET Web Form Page classes that require a default constructor.

A typical solution to these problems is to make such boundary class into a Humble Object, where all interesting logic is extracted from the boundary class into a component. The boundary class should only contain the call to the Service Locator and call one method on the resolved service. This minimizes the amount of untestable code in the application.

In all other cases, constructor injection should be preferred.

The fact however that you resolve an ILogger implies that your boundary class does too much. Instead this ILogger should be a constructor dependency of the component that you extracted from the boundary class to become a Humble Object.

Once you've done this, you won't be resolving ILogger directly anymore and this solves your problem; ILogger has become a dependency of a consumer and this ensures that Simple Injector is able to build the correct Logger<T> on your behalf.

When it comes to applying dependencies to exception filters in Web API (your particular case), a good solution is to create a proxy for your exception filters that will delegate the call to the real filter that gets resolved. This can be a bit of infrastructure and the concept is explained here.

If it is impossible to apply the above advise, for whatever reason, you can always request a Logger<T> directly from the container:

ILogger log = SimpleInjectorInitializer.Container.GetInstance<Logger<MyHumbleObject>>();
Steven
  • 166,672
  • 24
  • 332
  • 435