4

I have a generic class Logger<TProvider> where TProvider : ILogProvider which I'd like to configure dependency injection for with Autofac.

This class also has a constructor:

public Logger(LogType loggerType)

Which is currently used like this:

var logger = new Logger<Log4NetLogProvider>(LogType.CMS);

I was wondering if this could be dependency injected or will that not be possible due to type/constructor parameter required?

I'm aware of the RegisterGeneric method, e.g:

builder.RegisterGeneric(typeof(Logger<>)).AsSelf()

I was wondering how I can tell autofac which TProvider was passed in and also provide the constructor parameter LogType? Or is this not a good candidate for DI?

DGibbs
  • 14,316
  • 7
  • 44
  • 83
  • 1
    Would you expect that a logger constructed with LogType.CMS could be used interchangeably with a logger constructed with say LogType.Foo? If not then your design should communicate that with separate interfaces -- ICmsLogger & IFooLogger. Your service would then have dependency on specific interface. – Mike Hixson Sep 28 '18 at 06:12
  • @MikeHixson - Thats a really great idea I hadn't thought of – DGibbs Sep 28 '18 at 08:09
  • @MikeHixson This works but I have to specify all of the interface types against the logger class e.g: `public class Logger : IFileLogger, IKenticoLogger, IKenticoAndFileLogger, ISmtpLogger` which is a bit ugly. Is there a way to give it a 'base' interface e.g `ILogger` that each interface is based on? – DGibbs Sep 28 '18 at 11:14
  • It seems like the long list of interfaces has been in your software all along, just hidden in the design in the form of non-interchangeable instances of Logger. Now that they have surfaced in the design and are deemed "ugly" maybe its time to do something about it. Maybe reconsider having so many different kinds of loggers? Personally, I see nothing wrong with a long list of interfaces. – Mike Hixson Oct 01 '18 at 22:21
  • @MikeHixson - Maybe 'ugly' is the wrong word. It just seemed like it should be possible to simply specify 'ILogger' as the other interfaces all derive from it but I guess not. – DGibbs Oct 02 '18 at 08:58
  • I'm sure it is possible to do what you ask, but using something like a generic ILogger basically gets you back to where you started -- services need to specify which flavor of the dependency they need. Perhaps you just want to do this: https://autofaccn.readthedocs.io/en/latest/advanced/keyed-services.html#resolving-with-an-index – Mike Hixson Oct 02 '18 at 20:49

2 Answers2

3

You could try using the WithParameter extension when registering the type

//using named parameter
builder.RegisterGeneric(typeof(Logger<>))
    .AsSelf()
    .WithParameter("loggerType", LogType.CMS);

//OR - using typed parameter
builder.RegisterGeneric(typeof(Logger<>))
   .AsSelf()
   .WithParameter(new TypedParameter(typeof(LogType), LogType.CMS));

Reference Passing Parameters to Register

Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • I'm not sure if this would work as `LogType` can be any number of enum values, it wont always be `.CMS` for example – DGibbs Sep 25 '18 at 11:04
  • Is it possible to do this: https://docs.autofac.org/en/latest/register/parameters.html#parameters-with-lambda-expression-components but with generic type instead? like `BaseRepository` ??? – IbrarMumtaz Apr 03 '19 at 23:07
0

The way that I coped with your problem was the following:

I registered the Generic class as you have done it.

builder.RegisterGeneric(typeof(Logger<>));

After that I register it with a type like so:

 builder.RegisterType<Logger<TProvider>>()
                .As<ILogger<TProvider>>()
                .WithParameter("loggerType", LogType.CMS);

or you can do it with Typed parameter like so:

builder.RegisterType<Logger<TProvider>>()
                    .As<ILogger<TProvider>>()
                    .WithParameter(TypedParameter.From(LogType.CMS)));

With TProvider being replaced with the paramater i.e.

builder.RegisterType<Logger<LogProvider>>()
                        .As<ILogger<LogProvider>>()
                        .WithParameter(TypedParamater.From(LogType.CMS)));
Givko
  • 350
  • 2
  • 14
  • Unless there's something I'm missing, I'm not sure this will work - `loggerType` can be anything, it's not always `LogType.CMS`, there are many different values it can be and the same applies to the generic `TProvider`. – DGibbs Sep 25 '18 at 11:47
  • The first thing on top of my head is to call `builder.RegisterType>() .As>(); builder.RegisterType>() .As>(); ` for each logger you with ti inject. And you can pass the according `LogType` for each instance also. – Givko Sep 25 '18 at 12:05
  • Ahh okay. How would that look on the page (web forms)? E.g for services I have a public property `public IMockService MockService { get; set; }` which gets injected, how would I do this for the logger given that I need to provide a constructor argument? – DGibbs Sep 25 '18 at 12:12
  • Well this is a different scenario. If you wish to inject a property you can use property injection. [Link](https://autofaccn.readthedocs.io/en/latest/register/prop-method-injection.html) to autofac documentation on property injection. You can use `builder.RegisterType().WithProperty("PropertyName", propertyValue);` when registering a type which requires property injection to inject the property by name. – Givko Sep 25 '18 at 14:05
  • Or if the registered type is a [reflection component](https://autofaccn.readthedocs.io/en/latest/register/registration.html#register-registration-reflection-components) you can use `builder.RegisterType().PropertiesAutowired();` – Givko Sep 25 '18 at 14:06
  • Sorry, i wasn't clear. The logger gets injected as a property of the page (web forms), so it's not created in the traditional sense (injected as constructor parameter). So on my .aspx page I would have a `public Logger Log { get; set; }`. How would I pass the `LogType` in to this? – DGibbs Sep 25 '18 at 14:09
  • See the above two comments I wrote if they don't do you any good comment any question you may have :) . – Givko Sep 25 '18 at 14:11
  • [Similar question](https://stackoverflow.com/questions/15600440/how-to-use-property-injection-with-autofac) on stackoverflow. – Givko Sep 25 '18 at 14:12
  • Thanks - I see that I can pass a value via property injection but this is at registration time whereas it would need to happen on a case by case basis for this to be viable, for example some pages may require at `Log` with a `LogType.CMS` and others may require `LogType.Console` etc... – DGibbs Sep 25 '18 at 14:23