使用primitive-arguments构造函数注册一个类型?

Shi*_*mmy 6 c# dependency-injection prism ioc-container unity-container

我有一个类在其构造函数中有一些原始类型参数,如字符串等.

我应该如何使用统一容器注册类型?

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

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

这会被视为坏习惯吗?有更好的选择吗?

Ste*_*ven 15

尝试防止在构造函数中具有原始或难以解析类型的类设计.正如你已经从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;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在你可以让你LoginManager依赖LoginManagerConfiguration:

public LoginManager(IRegionManager regionManager,
    IEventAggregator eventAggregator,
    LoginManagerConfiguration configuration)
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

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

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

指定应用程序范围的配置对象而不是此类型特定的DTO可能很诱人,但这是一个陷阱.这种应用程序范围的配置对象是Service Locator反模式的配置等价物.不清楚类型需要什么配置值,并使类更难测试.

选项2:从该类派生

另一种选择是从该类派生,仅用于DI配置.当您无法更改类签名时(即当它是第三方组件时),这尤其有用:

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

将此类定义在应用程序的组合根附近.该类成为DI配置的实现细节.现在,您的类型注册将非常简单:

container.RegisterType<ILoginManager, DILoginManager>();
Run Code Online (Sandbox Code Playgroud)

虽然调用了延迟加载配置值,但是要非常小心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"));
}));
Run Code Online (Sandbox Code Playgroud)