当构造函数注入在配置和运行时已知参数时,从AutoFac获取类实例的最佳方法是什么

时间:2015-08-21 11:36:21

标签: c# configuration settings autofac

我发现了两个与我的问题相近的相关帖子,但仍然没有回答我正在寻找的内容。

Which design patterns can be applied to the configuration settings problem?

Should my program's "services" be responsible to get their own configuration?

我有一个使用AutoFac作为DI容器的应用程序并遇到了以下问题。我有在启动时向AutoFac注册的类,但是一些构造函数参数只在运行时才知道。我制作了一个样本来展示我的内涵。注释显示代码中我想要检索类实例的位置,并在此时注入一些配置数据。

它背后的想法是将通用设置提供程序的实现与某些类的配置分开。应从设置提供程序(ISettingsProvider)检索配置,转换为类相关的设置类(FooSettings),然后在创建时将其注入该类(Foo)。

有关如何执行此操作的任何建议吗?

我的样本:

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
    {
    }
}

2 个答案:

答案 0 :(得分:3)

当我需要将运行时值作为依赖项注入时,我总是使用Abstract Factory pattern

定义工厂界面

public interface IFooFactory
{
    Foo CreateFoo();
}

借助autofac提供的Delegate Factories支持实现界面。如果您依赖于任何委托,autofac将为您创建具有除委托所接受的依赖项之外的所有依赖项的实现。

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);
    }
}

依赖IFooFactory代替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:)
    }
}

当然注册工厂

builder.RegisterType<FooFactory>().As<IFooFactory>(); 

答案 1 :(得分:0)

您可以FooSettingsProvider取决于SettingsProvider并将lambda注册为FooSettings

例如:

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;
    }
}

要注册您的FooSettings,您可以使用此注册:

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