【问题标题】:resolving constructor dependency at runtime (without attributes)在运行时解析构造函数依赖(没有属性)
【发布时间】:2014-07-29 23:30:46
【问题描述】:

我正在使用 Unity 进行依赖注入,我想在运行时控制,解析哪个特定类型并将其作为依赖项传递给构造函数。

我有一个界面:

public interface IDatabase
{
    void DoSomething();
}

这是由两个类实现的:

public class SQLDatabase : IDatabase
{
    public void DoSomething()
    {
        //Do Something on a SQL server database...
    }
}
public class OracleDatabase : IDatabase
{
    public void DoSomething()
    {
        //Do Something on an Oracle database...
    }
}

第三个类依赖于 IDatabase

public class DataService: IDataService
{
    public DataService(IDatabase database)
    {
        database.DoSomething();
    }
}

模块向 Unity 注册每个类,并且为两个 IDatabase 类型指定特定名称,以便区分它们:

container.RegisterType<IDatabase, SQLDatabase>("SQLDatabase");
container.RegisterType<IDatabase, OracleDatabase>("OracleDatabase");
container.RegisterType<IDataService, DataService>();

我需要创建一个 Consumer 的实例,此时我想指定实现 IDatabase 的两种类型中的哪一种由 Unity 使用/注入,但我不知道如何告诉 Unity 哪种具体类型构建/解决?我想我想要这样的东西(伪代码):

public class Consumer
{
    IDataService dataService = null;
    public Consumer(string runtimeChoice)
    {
        if (runtimeChoice == "SQLSERVER")
        {
            dataService = _container.Resolve<IDataService>("SQLDatabase");
        }
        else if (runtimeChoice == "Oracle")
        {
            dataService = _container.Resolve<IDataService>("OracleDatabase");
        }
    }
}

那么,我如何告诉 Unity 解析 IDatabase 类型,使用特定的命名类型,并将其传递给依赖对象的构造函数,但在运行时执行?

【问题讨论】:

  • 使其成为可能返回已配置容器的方法的参数。
  • 您真的想在应用程序运行时切换数据库还是runtimeChoice 是在启动后固定的值(即放置在应用程序的配置文件中)?
  • 不,它不会在运行时更改,但需要在运行时选择。用户有多个可用源,因此代码需要在运行时处理选择。

标签: c# unity-container


【解决方案1】:

有两件事我会改变,我会尽量确保这些魔法字符串的处理尽可能接近它们的源,我会尽量确保我的代码与容器无关。

我会有以下界面:

public interface IDataServiceFactory
{
    IDataService CreateSqlDataService();
    IDataService CreateOracleDataService();
}

使用这样的实现:

public class DataServiceFactory : IDataServiceFactory
{
    private readonly Func<IDataService> _sqlDataServiceFactory;
    private readonly Func<IDataService> _oracleDataServiceFactory;

    public DataServiceFactory(Func<IDataService> sqlDataServiceFactory, Func<IDataService> oracleDataServiceFactory)
    {
        _sqlDataServiceFactory = sqlDataServiceFactory;
        _oracleDataServiceFactory = oracleDataServiceFactory;
    }

    public IDataService CreateSqlDataService()
    {
        return _sqlDataServiceFactory();
    }


    public IDataService CreateOracleDataService()
    {
        return _oracleDataServiceFactory();
    }
}

然后我会向您的IUnityContainer 注册,如下所示:

_container.RegisterType<IDataService, DataService>("SQLDataService", 
    new InjectionConstructor(new ResolvedParameter<IDatabase>("SQLDatabase")));

_container.RegisterType<IDataService, DataService>("OracleDataService", 
    new InjectionConstructor(new ResolvedParameter<IDatabase>("OracleDatabase")));

_container.RegisterType<IDataServiceFactory, DataServiceFactory>(new InjectionConstructor(
    new ResolvedParameter<Func<IDataService>>("SQLDataService"), 
    new ResolvedParameter<Func<IDataService>>("OracleDataService"));

现在,以前创建 Consumer 实例的任何内容现在都应该依赖于 IDataServiceFactory,并且应该处理运行时值以调用正确的方法 CreateSqlDataService()CreateOracleDataService()

您的所有运行时代码现在都与容器无关,魔术字符串在其源代码旁边处理。

【讨论】:

  • 谢谢。在这种情况下,没有真正的魔术字符串,我只是试图演示用户在运行时进行选择的情况,这会影响需要将哪个对象解析为依赖项。
【解决方案2】:

我能够使用 DependencyOverride 解析正确的类型,它本身从统一容器中获取所需的类型。所以消费者变成了:

public class Consumer
{
    IDataService dataService = null;
    public Consumer(string runtimeChoice)
    {
        DependencyOverride<IDatabase> dependency = null;

        if (runtimeChoice == "SQLSERVER")
        {
            dependency = new DependencyOverride<IDatabase>
                                 (Container.Resolve<IDatabase>("SQLDatabase"));

        }
        else if (runtimeChoice == "Oracle")
        {
            dependency = new DependencyOverride<IDatabase>
                                 (Container.Resolve<IDatabase>("OracleDatabase"));
        }            

        dataService = _container.Resolve<IDataService>(dependency);
    }
}

【讨论】:

    【解决方案3】:

    我有类似的要求,我需要在运行时解析“widgetbuilder”类型。在我的 Unity 配置中,我注册了两种类型的小部件构建器:-

            container.RegisterType<IWidgetBuilder, SinglePointWidgetBuilder>("SinglePointWidgetBuilder");
            container.RegisterType<IWidgetBuilder, CurrentVsTargetWidgetBuilder>("CurrentVsTargetWidgetBuilder");
    

    然后我在运行时通过以下方式解决它们:-

    var container = UnityConfig.GetConfiguredContainer();
    var widgetBuilder = container.Resolve<IWidgetBuilder>("SinglePointWidgetBuilder");
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多