7

We use Simple Injector in ASP.NET Core application. Recently we've decided to use Serilog for logging purposes.

Configuration was done in Program.cs as stated in their documentation. Then, in order to make Simple Injector able to resolve ILoggerFactory I did something like this:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    app.UseMvc();

    // As per my understanding, we force SimpleInjector to use 
    // ILoggerFactory registration from IApplicationBuilder
    container.CrossWire<ILoggerFactory>(app)
}

After that ILoggerFactory can be injected wherever we use it. And it works fine (creates new instances ILogger<T>).

But this is annoying, to create instance from factory every time we need. It would be better to get ILogger<T> directly in dependent class, instead of ILoggerFactory.

So, how can I register ILogger<T> to get instance in case like below?

public class HelloWorldController : Controller
{
    public HelloWorldController(ILogger<HelloWorldController> logger)
    {
         // ...
    }
}
Steven
  • 166,672
  • 24
  • 332
  • 435
managerger
  • 728
  • 1
  • 10
  • 31

2 Answers2

7

Although Alsami's answer would work, use the following registration instead:

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

// This next call is not required if you are already calling AutoCrossWireAspNetComponents
container.CrossWire<ILoggerFactory>(app);

This exact example is shown in the documentation.

This registration allows injecting the Logger<T> into a non-generic ILogger constructor argument, where the T of Logger<T> becomes the type the logger is injected into. In other words, when HelloWorldController depends on ILogger, it will get injected with a Logger<HelloWorldController>. This means you can simplify your HelloWorldController to the following:

public class HelloWorldController : Controller
{
    public HelloWorldController(ILogger logger)
    {
         // ...
    }
}

By letting your application components depend on ILogger rather than ILogger<T> you:

  • Simplify your application code
  • Simplify your unit tests
  • Remove the possibility of making accidental errors, because it becomes impossible to inject the wrong logger.
Steven
  • 166,672
  • 24
  • 332
  • 435
  • It is a bad idea to register the logger as singleton just because the creation of the logger is heavy and you have to pass a type, that ieven has a purpose and should be passed. https://stackoverflow.com/a/8485625/5397642 – alsami Mar 15 '18 at 14:29
  • 1
    `Logger` is thread-safe and can be safely registered as singleton. As a matter of fact, ASP.NET Core itself registers it as singleton. – Steven Mar 15 '18 at 14:35
3

You also have to register the logger iteself as a generic type. I don't know simple injector but this should be the correct syntax.

container.Register(typeof(ILogger<>), typeof(Logger<>), Lifestyle.Singleton);
Steven
  • 166,672
  • 24
  • 332
  • 435
alsami
  • 8,996
  • 3
  • 25
  • 36