IoC容器中的命名服务 - 一个坏主意?

Law*_*eld 5 .net c# dependency-injection ninject ioc-container

在编写容器时使用服务密钥(或"命名服务")似乎是个坏主意.

使用命名服务要求我们使用匹配的键注释我们的构造函数参数(从而与容器耦合),或者为我们的每个服务执行额外的连接(从而从容器中丢失大量自动化).

例如,我目前有以下接口,由以下类实现:

  • IListSerializer
    • CheckboxListSerializer
    • TreeViewListSerializer

我也有无数的类依赖于这些类中的一个或两个.但是,AFAIK我应该引用IListSerializer作为我的依赖而不是实现.这意味着我必须使用键/名来区分它们,这是它开始变得丑陋的地方.

我可以看到我的选项是以下之一:

  • 使用键注释构造函数参数(依赖项).与IoC Container结合使用.
  • 在组合根中执行手动接线.添加重复膨胀.
  • 引用类而不是接口.看起来像是为了满足IoC容器的黑客攻击.

有什么建议?

Nic*_*rdt 9

通常,在为IoC设计组件和服务时,Liskov替换原则是非常有用的指南.如果服务的两个实现在运行时不能互换使用,那么该服务过于笼统而无意义.在这种情况下,我会考虑使用类似的东西,IListSerializer<T>如果这是一个选项.

但是,如果要使用命名服务,则使用Autofac进行设置非常简单且不引人注目.

首先,使用其名称注册每个序列化程序:

builder.RegisterType<CheckBoxListSerializer>()
    .Named<IListSerializer>("checkBoxSerializer");
builder.RegisterType<TreeViewListSerializer>()
    .Named<IListSerializer>("treeViewSerializer");
Run Code Online (Sandbox Code Playgroud)

然后,添加一个全局可用的参数,该参数使用构造函数参数名称来选择正确的实现.我们可以用一个模块做到这一点:

class NamedParameterResolutionModule<TService> : Module
{
    Parameter _attachedParameter = new ResolvedParameter(
        (pi, c) => pi.ParameterType == typeof(TService),
        (pi, c) => c.ResolveNamed<TService>(pi.Name));

    protected override void AttachToComponentRegistration(
        IComponentRegistry registry,
        IComponentRegistration registration)
    {
        registration.Preparing += (s, e) => {
            e.Parameters = new[] { _attachedParameter }.Contact(e.Parameters);
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

像这样注册模块:

builder.RegisterModule<NamedParameterResolutionModule<IListSerializer>>();
Run Code Online (Sandbox Code Playgroud)

然后,组件将根据构造函数参数名称获取序列化程序:

class SomeComponent : ...
{
    public SomeComponent(IListSerializer checkBoxSerializer) { ...
}
Run Code Online (Sandbox Code Playgroud)

  • 太棒了 - 很高兴它有所帮助! (2认同)