5

我有一个类在其构造函数中有一些基本类型参​​数,如字符串等。用原始参数构造函数注册一个类型?

我应该如何注册与统一容器的类型?

public LoginManager(
    IRegionManager regionManager, 
    IEventAggregator eventAggregator, 
    string mainRegionName, 
    Uri login, 
    Uri target) 
    { 
    this.regionManager = regionManager; 
    this.eventAggregator = eventAggregator; 
    this.mainRegionName = mainRegionName; 
    this.login = login; 
    this.target = target; 
    } 
} 

更新
Remeber的IRegionManagerIEventAggregator已知类型的棱镜UnityBootstrapper这是在我的情况下,容器包装。我必须重新注册吗?我想尽可能简化类型注册。

这会被认为是一个坏习惯吗?任何更好的选择?

回答

12

说实话,我会尽量避免在构造函数中有一个原始类或难以解析类的类设计。正如你已经从Tavares的回答中看到的那样,你的配置变得非常脆弱(更新:Tavares似乎已经删除了他的回答,原因是我不清楚)。您会减少编译时支持,并且每次更改该构造函数都会使您更改DI配置。

有多种方法可以更改您的设计以防止出现这种情况。哪一个是适用于你的是你的,但这里有一些想法:

方法1:使用一个不变的配置DTO:

private sealed class LoginManagerConfiguration 
{ 
    public Uri Login { get; private set; } 
    public Uri Target { get; private set; } 
    public string MainRegionName { get; private set; } 

    public LoginManagerConfiguration(Uri login, Uri target, string mainRegionName) 
    { 
     this.Login = login; 
     this.Target = target; 
     this.MainRegionName = mainRegionName; 
    } 
} 

现在你可以让你的LoginManager采取的依赖LoginManagerConfiguration

public LoginManager(IRegionManager regionManager, 
    IEventAggregator eventAggregator, 
    LoginManagerConfiguration configuration) 
{ 
    ... 
} 

LoginManagerConfiguration可以简单地注册这样的:

container.RegisterInstance<LoginManagerConfiguration>(
    new LoginManagerConfiguration(
     login: new Uri("Login"), 
     target: new Uri("Target"), 
     mainRegionName: ConfigurationManager.AppSettings["MainRegion"])); 

指定应用程序范围的配置对象而不是特定类型的DTO可能很诱人,但这是一个陷阱。这种应用程序范围的配置对象是服务定位器反模式的配置等同物。变得不清楚类型需要什么样的配置值,并使得类难以测试。

选项2:从该类

另一种选择是从类派生推导,只是为了DI构造的目的。当你不能改变类签名,这是特别有用的(即当它是一个第三方组件):

private sealed class DILoginManager : LoginManager 
{ 
    DILoginManager(IRegionManager regionManager, 
     IEventAggregator eventAggregator) 
     : base(regionManager, eventAggregator, 
      ConfigurationManager.AppSettings["MainRegion"], 
      new Uri("Login"), 
      new Uri("Target")) 
    { 
     ... 
    } 
} 

定义这个类接近你的应用程序的组成根源。该类成为DI配置的实现细节。你的类型的注册,现在将是非常简单的:

container.RegisterType<ILoginManager, DILoginManager>(); 

要非常小心,虽然与调用,延迟加载配置值,如ConfigurationManager.AppSettings["MainRegion"]。这很容易导致在应用程序启动过程中配置错误未被捕获的情况,这确实是最好的。

方法3:使用一个工厂委托

最后的选择我想现在是一个工厂。这看起来非常像Traveses的答案,但是更安全:

var mainRegion = ConfigurationManager.AppSettings["MainRegion"]; 

container.Register<ILoginManager>(new InjectionFactory(c => 
{ 
    return new LoginManager(
     c.Resolve<IRegionManager>(), 
     c.Resolve<IEventAggregator>(), 
     ConfigurationManager.AppSettings["MainRegion"], 
     new Uri("Login"), 
     new Uri("Target")); 
})); 

我希望这有助于。

+0

对于InjectionFactory方法,您还可以注册一个类型为Func 的关联接受这些参数的InjectionFactory并返回一个ILoginManager,如下所示:new InjectionFactory(c =>(login,target ,mainRegionName)=> c.Resolve (新的ParameterOverride(“mainRegionName”,mainRegionName),新的ParameterOverride(“login”,登录),新的ParameterOverride(“target”,target))))。通过这种方式进行解析,您可以在构造函数中保持依赖关系的灵活性,并且只需对基本参数更改进行更改即可。 – 2011-06-01 22:03:33

+1

非常好的答案,顺便说一句。 – 2011-06-01 22:03:52

+0

忘记添加:使用方法是,您可以接受Func 作为依赖项,并将这些参数传递给该函数,从而生成ILoginManager。如果这些是设置,我会坚持使用Steven的方法,但是如果它们的运行时值可能会在某些情况下发生变化,那么您可能需要这样做。 – 2011-06-01 22:06:01

0

不,这不是一个坏习惯。这是一个非常有效的场景。从Unity开始已经过去几年了,但从头顶开始,你必须明确地指出你想要的构造函数,并枚举所有的参数,而对于原始的构造函数,请做new ResolvedParameter("your value")

另外我注意到你有一个Type参数。对Unity的态度要谨慎,因为它具有......非常令人惊讶的处理方式。我有一个博客详细说明here

+0

dziękuję。请看看我更新的问题。如果您能提供我的特例,我将不胜感激。顺便说一句,哪里是*我的头顶*?我认为它被称为堆? – Shimmy 2011-05-15 21:37:39

+0

绝大多数情况下,您不需要明确使用ResolvedParameter来处理基元。 – 2011-05-15 21:43:38

相关问题