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

Inject dependency in injected dependency with different InjectionConstructor parameters

我正在使用一个有用的 RestClient,它处理对其他 Web 服务的 Rest 调用。这个 RestClient 是更大的服务客户端的一部分,它通过提供业务逻辑等使用的便利方法来处理不同的 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 实现此目的?

你必须为此使用工厂。如果 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 注入工厂而不是 RestClient。

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

除了你自己想出来的,还有一个办法就是在解析的时候用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)
);

恕我直言,您将 "inject dependencies" 与 "inject parameters" 混合在一起。如果您的代码是新的,那么这是一个不太理想的设计。

对于新代码,您应该(恕我直言)注入依赖项。

..

现在,你是如何处理你的需求的?

有办法。

将 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 用于处理 "old code" 在构造函数中具有参数的变通方法。并且 InjectionConstructor 不应该被设计成新的代码。再次,恕我直言。