在运行时解决构造函数依赖(不带属性)

本文关键字:属性 函数依赖 运行时 解决 | 更新日期: 2023-09-27 18:15:25

我使用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...
    }
}

第三个类依赖于iddatabase

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

模块在Unity中注册每个类,两个iddatabase类型被赋予特定的名称,以便它们可以区分:

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

我需要创建一个Consumer的实例,在这一点上,我想指定两种类型中的哪一种实现iddatabase将被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解析iddatabase类型,使用特定的命名类型,并将其传递给依赖对象的构造函数,但在运行时执行?

在运行时解决构造函数依赖(不带属性)

有两件事我要改变,我会尽量确保那些神奇的字符串处理尽可能接近他们的源,我会尽量确保我的代码是容器无关的。

我将有以下接口:

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()

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

我能够使用DependencyOverride解决正确的类型,它本身从unity容器中获得所需的类型。因此消费者变成:

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

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

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

然后在运行时解析它们:-

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