繁体   English   中英

IoC、依赖注入和构造函数参数

[英]IoC, Dependency injection and constructor arguments

我有一个我希望能够根据控制反转原则创建的服务,所以我创建了一个接口和一个服务类。

public interface IMyService
{
    void DoSomeThing1();
    void DoSomeThing2();
    void DoSomeThing3();
    string GetSomething();

}

public class MyService : IMyService
{
    int _initialValue;
    //...

    public MyService(int initialValue)
    {
        _initialValue = initialValue;
    }

    public void DoSomeThing1()
    {
        //Do something with _initialValue
        //...
    }

    public void DoSomeThing2()
    {
        //Do something with _initialValue
        //...
    }

    public void DoSomeThing3()
    {
        //Do something with _initialValue
        //...
    }

    public string GetSomething()
    {
        //Get something with _initialValue
        //...
    }
}

例如使用 Unity 我可以设置我的 IoC。

public static class MyServiceIoc
{
    public static readonly IUnityContainer Container;

    static ServiceIoc()
    {
        IUnityContainer container = new UnityContainer();
        container.RegisterType<IMyService, MyService>();
        Container = container;
    }
}

问题是构造函数参数。 我可以使用 ParameterOverride 之类的

var service = MyServiceIoc.Container.Resolve<IMyService>(new ParameterOverrides
{                                                                                   
    {"initialValue", 42}
});

但我不想使用丢失类型的参数。 如果有人更改构造函数参数名称或添加一个参数怎么办? 他不会在完整的时间收到警告,也许除了最终用户之外没有人会发现它。 也许程序员为测试更改了他的 IoC 设置,但为了“发布”使用而忘记了它,那么即使是具有 100% 代码覆盖率的代码库也无法检测到运行时错误。

可以向接口和服务添加 Init 函数,但是服务的用户必须理解这一点,并记住每次获取服务实例时调用 init 函数。 该服务变得不那么自我解释,并且对不正确的使用开放。 如果方法不依赖于调用它们的顺序,我最好。

让它更安全的一种方法是在 Ioc 上有一个创建函数。

public static class MyServiceIoc
{
    //...
    public IMyService CreateService(int initialValue)
    {
        var service = Container.Resolve<IMyService>();
        service.Init(initialValue);

    }
}

但是如果你只看服务和它的接口,上面提到的问题仍然适用。

有没有人对这个问题有一个强大的解决方案? 如何在仍然使用 IoC 的情况下以安全的方式将初始值传递给我的服务?

DI 容器是基于反射的,并且基本上是弱类型的。 这个问题比原始依赖要广泛得多——它无处不在。

一旦您执行以下操作,您就已经失去了编译时安全性:

IUnityContainer container = new UnityContainer();
container.RegisterType<IMyService, MyService>();
var service = container.Resolve<IMyService>(new ParameterOverrides
{                                                                                   
    {"initialValue", 42}
});

问题是您可以删除第二个语句,并且代码仍然可以编译,但现在它将不再起作用:

IUnityContainer container = new UnityContainer();
var service = container.Resolve<IMyService>(new ParameterOverrides
{                                                                                   
    {"initialValue", 42}
});

请注意,编译时安全性的缺乏与具体依赖关系无关,而是与涉及 DI 容器的事实有关。

这也不是 Unity 的问题; 它适用于所有 DI 容器。

某些情况下,DI 容器可能有意义,但在大多数情况下,纯 DI是一种更简单、更安全的替代方案:

IMyService service = new MyService(42);

在这里,如果其他人在您看向别处时更改了 API,您将收到编译器错误。 这很好: 编译器错误比运行时错误给你更直接的反馈


顺便说一句,当您传入 Primitive Dependency 并无形地将其转换为Concrete Dependency 时,会使客户端更难以理解发生了什么。

我建议改为这样设计:

public class MyService : IMyService
{
    AnotherClass _anotherObject;
    // ...

    public MyService(AnotherClass anotherObject)
    {
        _anotherObject = anotherObject;
    }

    // ...
}

使用 Pure DI 组合仍然很容易且类型安全:

IMyService service = new MyService(new AnotherClass(42));

如何在仍然使用 IoC 的情况下以安全的方式将初始值传递给我的服务?

您可以在使用IUnityContainer.RegisterInstance方法在 Unity 中注册时显式调用类型的构造函数:

container.RegisterInstance<IMyService>(new MyService(42));

这将为您提供您提到的编译时安全性,但代价是它只会被实例化一次,并且会立即创建(而不是第一次请求时)。

您也许可以通过使用接受 LifetimeManager 类的方法重载之一来处理这个缺点。

这取决于您的用例,但在 IoC 容器世界中,它可能如下所示:

public class MyService : IMyService
{
    int _initialValue;
    // ...

    public MyService(IConfigurationService configurationService)
    {
        _initialValue = configurationService.GetInitialValueForMyService();
    }
    
    // ...
}

如果您的带有构造函数参数的类在您的代码之外(例如在第 3 方库中),您可以使用适配器。

public class AdaptedMyService : MyService
{
    public AdaptedMyService(IConfigurationService configurationService)
        : base(configurationService.GetInitialValueForMyService())
    {
    }
}

然后像这样在 IoC 容器中注册适配的类:

container.Register<IMyService, AdaptedMyService>();

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM