使用不同的InjectionConstructor参数的注入依赖项中的注入依赖项

时间:2019-08-20 06:43:18

标签: dependency-injection unity-container

我正在使用一个有用的RestClient,它处理对其他Web服务的Rest调用。此RestClient是较大的Service-Client的一部分,后者通过提供由businesslogic等使用的便捷方法来处理不同的Web服务。 我正在使用UnityContainers进行依赖项注入。

我有某种RestClient:

public interface IRestClient
{
    T PostSomething<T>();
}

public class RestClient : IRestClient
{
    public RestClient(string baseUrl) { }
    public T PostSomething<T>() { /* post something */ }
}

我在多个ServiceClient中使用:

public interface IServiceClient1
{
    void Work1();
}

public class ServiceClient1 : IServiceClient1
{
    private RestClient client;
    private string myRestUrl = "ServiceUrl1";

    public ServiceClient1(IRestClient client)
    {
        this.client = client;
        client.SetUrl(myRestUrl); // i don't want to do this here. This should be done by unity when RestClient is instantiated
    }
    public void Work1()
    {
        /* do something */
        client.PostSomething<string>();
    }
}

public interface IServiceClient2
{
    void Work2();
}

public class ServiceClient2 : IServiceClient2
{
    private RestClient client;
    private string myRestUrl = "ServiceUrl2";

    public ServiceClient2(IRestClient client)
    {
        this.client = client;
        client.SetUrl(myRestUrl); // i don't want to do this here. This should be done by unity when RestClient is instantiated
    }
    public void Work2()
    {
        /* do something */
        client.PostSomething<string>();
    }
}

现在我想用UnityContainers注入这些类:

container.RegisterType<IServiceClient1, ServiceClient1 >();
container.RegisterType<IServiceClient2, ServiceClient2 >();

// And here comes the problem:
container.RegisterType<IRestClient, RestClient>(new InjectionConstructor("ThisParameterShouldBeSetByServiceClient"));

ServiceClients两者都大不相同,如本例所示。此示例可能仅显示问题。

我想要注册类型RestClient,并且我希望ServiceClientXRestClient的构造方法选择字符串参数。而且我不想在ServiceClient的构造函数中设置Url。

如何使用UnityContainers实现这一目标?

3 个答案:

答案 0 :(得分:0)

您必须为此使用工厂。如果ServiceClient决定如何创建RestClient:

public class ServiceClient1 : IServiceClient1
{
    private RestClient client;
    private string myRestUrl = "ServiceUrl1";

    public ServiceClient1(IRestClientFactory clientFactory)
    {
        this.client = clientFactory.CreateClient(myRestUrl);
    }
    public void Work1()
    {
        /* do something */
    }
}

Unity注入Factory,而不注入RestClient。

如果要模拟RestClient,则应将工厂切换为模拟工厂。

答案 1 :(得分:0)

除了您自己想出的方法之外,另一种方法是在解析过程中使用ParameterOverride

container.RegisterType<IRestClient, RestClient>(url1, new ParameterOverride(url1));
IRestClient restClient1 = container.Resolve<IRestClient>(url1);
container.RegisterType<IServiceClient1, ServiceClient1>();
IServiceClient1 svcClient = container.Resolve<IServiceClient1>(
    new ParameterOverride("client", restClient1)
);

答案 2 :(得分:0)

恕我直言,您正在将“注入依赖项”与“注入参数”混合在一起。如果您的代码是新的,那么这将是一个不太理想的设计。

使用新代码,您应该(恕我直言)注入依赖关系。

..

现在,您如何处理自己的需求?

有办法。

将IConfigurationRetriever注入到ServiceClient1和ServiceClient2中。

public ServiceClient1 (IRestClient client, IConfigurationRetriever icr)

现在您可以创建一个简单的实现

public interface IConfigurationRetriever()
{
    string GetSomeMagicValueOne();
}


public InMemoryConfigurationRetriever : IConfigurationRetriever
{
    public string GetSomeMagicValueOne(){ return "HardCodedUrl"; }
}

您不应该将URL硬编码到对象中。但是,至少在以上情况下,您已经以某种方式进行了硬编码,从而仍可以使ServiceClient1和ServiceClient2在将来保持灵活性。

以及如何,当您想创建其他方式来读取配置时,可以编写“ FromAppOrWebConfigConfigurationRetriever:IConfigurationRetriever”或“ FromDotNetCoreJsonFileConfigurationRetriever:IConfigurationRetriever”或“ FromTheDatabaseConfigurationRetriever:IConfigurationRetriever”

恕我直言,InjectionConstructor用于处理在构造函数中具有参数的“旧代码”,这是一种变通方法。而且不应将InjectionConstructor设计成新代码。再次,恕我直言。