What is the best way to get an instance of a class from AutoFac when the constructor injection has parameters known in configuration and runtime

I found two related posts that are close to my question, but still don't answer what I'm looking for.

What design patterns can be applied to a problem with configuration settings?

Should my services “services” be responsible for their own configuration?

I have an application using AutoFac as a DI container and have encountered the following problem. I have classes that I register with AutoFac at startup, but some constructor options are known only at runtime. I made a sample to show my intention. The comment shows the location in the code where I want to get an instance of the class and enter configuration data at this point in time.

The idea is to separate the implementation of the general settings provider from the configuration of certain classes. The configuration must be obtained from the settings provider (ISettingsProvider), converted to the class of parameters associated with the class (FooSettings), and then will be introduced at creation time into this class (Foo).

Any suggestions on how to do this?

My example:

using System; using System.Collections.Generic; using System.Linq; using System.Web; using Autofac; namespace FooNamespace { internal static class Program { private static void Main(string[] args) { var builder = new ContainerBuilder(); builder.RegisterType<SettingsProvider>().As<ISettingsProvider>().SingleInstance(); builder.RegisterType<AnotherService>().As<IAnotherService>().SingleInstance(); builder.RegisterType<Foo>(); builder.RegisterType<ClientClass>(); var container = builder.Build(); var myClient = container.Resolve<ClientClass>(); myClient.DoSomething(); } } public class Foo { private FooSettings _settings; private IAnotherService _anotherService; public Foo(IAnotherService anotherService, FooSettings settings) { _anotherService = anotherService; _settings = settings; } } public class FooSettings { public string SettingA { get; private set; } public string SettingB { get; private set; } public FooSettings(string settingA, string settingB) { SettingA = settingA; SettingB = settingB; } } public class ClientClass { private readonly ISettingsProvider _settingsProvider; public ClientClass(ISettingsProvider settingsProvider) { _settingsProvider = settingsProvider; } public void DoSomething() { var providerSettings = _settingsProvider.GetSettings("fooSettings"); var fooSettings = new FooSettings(providerSettings[0], providerSettings[1]); // what is the best way to get an instance of foo with the fooSettings set at runtime // when Foo and IAnotherService have been registered with AutoFac } } public interface ISettingsProvider { string[] GetSettings(string settingsGroup); } class SettingsProvider : ISettingsProvider { public string[] GetSettings(string settingsGroup) { return new string[2] {"valueA", "valueB"}; } } public interface IAnotherService { } class AnotherService : IAnotherService { } } 
+4
source share
2 answers

I use the Abstract Factory pattern whenever I need to get the value of the runtime has been introduced as a dependency.

Define Factory Interface

 public interface IFooFactory { Foo CreateFoo(); } 

Deploy the interface using autofac delegate support. If you are running a dependency on any delegate, autofac will create an implementation for you with all the dependencies except the dependencies that the delegate accepts.

 public class FooFactory : IFooFactory { private readonly Func<FooSettings, Foo> creationFunc; private readonly ISettingsProvider settingsProvider; public FooFactory(Func<FooSettings, Foo> creationFunc, ISettingsProvider settingsProvider) { if (creationFunc == null) throw new ArgumentNullException("creationFunc"); if (settingsProvider == null) throw new ArgumentNullException("settingsProvider"); this.creationFunc = creationFunc; this.settingsProvider = settingsProvider; } public Foo CreateFoo() { var providerSettings = settingsProvider.GetSettings("fooSettings"); var fooSettings = new FooSettings(providerSettings[0], providerSettings[1]); return creationFunc(fooSettings); } } 

Take a dependency on IFooFactory instead of ISettingsProvider

 public class ClientClass { private readonly IFooFactory fooFactory; public ClientClass(IFooFactory fooFactory) { this.fooFactory = fooFactory; } public void DoSomething() { var foo = fooFactory.CreateFoo(); //Do whatever with foo //Your foo is now injected with another service and settings too:) } } 

And of course, register a factory

 builder.RegisterType<FooFactory>().As<IFooFactory>(); 
+2
source

You can have a FooSettingsProvider which will depend on SettingsProvider and register the lambda as FooSettings .

For instance:

 public interface ISettingsProvider<TSettings> where TSettings : ISettings { TSettings Settings { get; } } public class FooSettingsProvider : ISettingsProvider<FooSettings> { public FooSettingsProvider(SettingsProvider settingsProvider) { this._settingsProvider = settingsProvider; this._settings = new Lazy<FooSettings>(this.InitializeSettings); } private readonly SettingsProvider _settingsProvider; private readonly Lazy<FooSettings> _settings; public FooSettings Settings { get { return this._settings.Value; } } private FooSettings InitializeSettings() { var providerSettings = this._settingsProvider.GetSettings("fooSettings"); var fooSettings = new FooSettings(providerSettings[0], providerSettings[1]); return fooSettings; } } 

To register FooSettings you can use this registration:

 builder.RegisterType<FooSettingsProvider>() .As<ISettingsProvider<FooSettings>>(); builder.Register(c => c.Resolve<ISettingsProvider<FooSettings>>().Settings) .As<FooSettings>(); 
0
source

All Articles