2015-10-07 137 views
3

我有一个服务,我希望能够根据控制反转原理创建,所以我创建了一个接口和一个服务类。IoC,依赖注入和构造函数参数

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

问题是构造函数参数。我可以使用参数覆盖像

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

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

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

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

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

    } 
} 

但是,如果您只关注服务及其接口,上述问题仍然适用。

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

回答

3

DI容器是基于反射的,基本上是弱类型的。问题比Primitive Dependencies广泛得多 - 它无处不在。

只要你做的东西像下面,你已经失去了编译时的安全性:

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

注意,缺乏编译时的安全性无关,与具体的依赖,但一个事实,即d我参与了I Container。

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

There are cases where a DI Container may make sense,但在大多数情况下,Pure DI是一个简单和更安全的替代:

IMyService service = new MyService(42); 

在这里,你会得到一个编译器错误,如果有人当你望着远处的其他人更改API。这很好:compiler errors give you more immediate feedback than run-time errors


顺便说一句,当你在一个原始依赖传递,无形中把它变成一个Concrete Dependency,你变得更加困难的客户,了解发生了什么事情。

我建议这样的设计,而不是它:

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

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

    // ... 
} 

这仍然是很简单的类型安全与纯DI组成:

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

谢谢!你关于AnotherClass的陈述可能是正确的,但这不是我关心的,这只是一个例子,因此我已经从我的例子中删除了它。所以它不会被误导。抱歉给你带来不便! – AxdorphCoder

1

如何传递初始以仍然使用IoC的安全方式向我的服务提供价值?

您可以显式调用类的构造函数,同时使用IUnityContainer.RegisterInstance方法统一注册吧:

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

这会给你你提到的编译时的安全性,但付出的代价是,它会仅实例化一次,并且将立即创建(而不是首次请求时)。

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

+0

谢谢,但我必须能够创建具有不同初始值的多个实例。 – AxdorphCoder