2

I am switching a project from SimpleInjector to Microsoft's built-in DI container. In this project, I have created a generic Log4NetAdapter class which I am registering into SimpleInjector's container against Log4Net's non-generic ILog interface like so :

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

This allows my controllers to receive a non-generic ILog injection in their controllers. I am trying to achieve the same thing with Microsoft's built-in DI (Microsoft.Extensions.Hosting) and I am not sure how to proceed or if this is even possible. I have tried the following :

services.AddTransient(typeof(ILog), typeof(Log4NetAdapter<>));

The above code is throwing the following exception :

System.ArgumentException: 'Cannot instantiate implementation type 'SomeTool.Utilities.Log4NetAdapter`1[T]' for service type 'log4net.ILog'.'

Is there a way to get this to work with Microsoft's DI container?

Thanks

Steven
  • 166,672
  • 24
  • 332
  • 435
Martin
  • 1,977
  • 5
  • 30
  • 67
  • How would you expect it to determine the `T` in `Log4NetAdapter` when requesting an instance of `ILog` though? – DavidG Aug 01 '22 at 13:39
  • SimpleInjector does it just fine, I was under the impression that there should be a way for Microsoft's DI to do the same. – Martin Aug 01 '22 at 14:03
  • 1
    Is your goal that when a class `Foo` has an `ILog` injected, the latter should be an instance of `Log4NetAdapter`? – Jonas Høgh Aug 01 '22 at 14:11
  • Exactly, All my services and controllers receive just an ILog in their constructors and I would love to be able to keep it that way. SimpleInjector offered this neat one-liner and I cannot find an equivalent for Microsoft's DI. – Martin Aug 01 '22 at 14:13
  • That ILog instance being a Log4NetAdapter in disguise is also important – Martin Aug 01 '22 at 14:15
  • 1
    @Martin MS.DI does not have that functionality built in. You could however use the Simple Injector provider to get the desired functionality. – Nkosi Aug 01 '22 at 14:28
  • Given that my goal is to move away from SimpleInjector, that is not an option. – Martin Aug 01 '22 at 14:36
  • 1
    @Martin understood. Then the answer to your post is that it is not currently possible to do what you want with the built in DI container in it's current version. – Nkosi Aug 01 '22 at 14:41
  • This is a .NET 4 application therefore no Microsoft.Extensions.Logging.Log4Net.AspNetCore package. Do I have to switch to .NET core and use ILogger with this package (using services.AddLogging(c => c.AddLog4Net()); along with this package) or is there a somewhat acceptable workaround? I was not planning on replacing the ILog injections with ILog but I'll do it if I have to... – Martin Aug 01 '22 at 14:45
  • 1
    @Martin Based on the current state of the framework, you will have to refactor to go the generics route. – Nkosi Aug 01 '22 at 14:50
  • the ILog interface is not generic though, how do I go around that? – Martin Aug 01 '22 at 15:13
  • `ILog` is what I was referring to when I said to use generics. `services.AddTransient(typeof(ILog<>), typeof(Log4NetAdapter<>));`. This however would require refactoring the constructors to use the appropriate interface. While you were not planning to do that change, you will have to if you want to use the built in container. – Nkosi Aug 01 '22 at 15:26
  • As I said, ILog is not generic, it cannot be used as such – Martin Aug 01 '22 at 15:27

1 Answers1

1

What you are trying to achieve is called Conditional Binding or Context-Based Injection and it is a feature that isn't supported by MS.DI and, due to its design, is impossible add.

More mature and feature rich DI Containers do support this feature, such as (but not limited to) Autofac, Ninject, and Simple Injector.

Steven
  • 166,672
  • 24
  • 332
  • 435