使用基元参数构造函数注册类型

本文关键字:构造函数 注册 类型 参数 | 更新日期: 2023-09-27 17:56:44

我有一个类,它的构造函数中有一些原始类型参数,例如字符串等。

我应该如何将类型注册到 unity 容器?

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

更新
请注意,IRegionManagerIEventAggregator是Prism UnityBootstrapper的已知类型,在我的情况下是容器包装器。我必须重新注册它们吗?我想使类型注册尽可能简单。

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

使用基元参数构造函数注册类型

尽量防止类设计在构造函数中具有原始或难以解析的类型。正如你已经从塔瓦雷斯的回答中看到的那样,你的配置变得非常脆弱(更新:塔瓦雷斯似乎已经删除了他的答案,原因我不清楚)。您失去了编译时支持,对该构造函数的每次更改都会使您更改 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"));
}));
不,

这不是一个坏习惯。这是一个完全有效的方案。自从我离开 Unity 以来已经有几年了,但从我的头顶上,您必须明确指向您想要的构造函数并枚举所有参数,对于原始参数,new ResolvedParameter("your value") .

我还注意到你那里有一个Type参数。对 Unity 要谨慎,因为它有一个...处理这些非常令人惊讶的方式。我有一篇博客文章在这里详细介绍了这一点。