如何在 Simple Injector 中使用 Ninject 的命名绑定?

How to use Ninject's Named bindings in Simple Injector?

我有一个使用 Ninject 作为 DI 的 MVC 应用程序,我正计划将其迁移到 Simple Injector。但是我找不到任何替代方法来用 Simple Injector 替换 Ninject 的命名绑定。

绑定:

    kernel.Bind<IStudent>().To<Student1>().Named("Senior");
    kernel.Bind<IStudent>().To<Student2>().Named("Junior");
    kernel.Bind<IStudent>().To<Student3>().Named("Junior");

控制器:

public class StudentController
{
  private readonly IEnumerable<IStudent> _seniors;
  private readonly IEnumerable<IStudent> _juniors;
  public StudentController([named("Senior")] IEnumerable<IStudent> seniors,[named("Junior")] IEnumerable<IStudent> juniors)
  {
    _seniors=seniors;
    _juniors=juniors;
  }
}

我提到了几个链接 How to use Ninject's Named bindings in Simple Injector。但是没有任何运气。

有多种选择:

选项 1:使用 [named] 属性

要实现这一点,您将必须重新创建 NamedAttribute,因为这不存在于简单注入器中,for good reason

您可以为collection名学生进行以下两种有条件的报名:

container.RegisterConditional<IEnumerable<IStudent>>(
    container.Collection.CreateRegistration<IStudent>(
        typeof(Student1)),
    c => c.Consumer.Target.GetCustomAttribute<namedAttribute>()?.Name == "Senior");

container.RegisterConditional<IEnumerable<IStudent>>(
    container.Collection.CreateRegistration<IStudent>(
        typeof(Student2),
        typeof(Student3)),
    c => c.Consumer.Target.GetCustomAttribute<namedAttribute>()?.Name == "Junior");

每个条件注册都为 IStudent collection 包装一个 Registration。谓词过滤目标,在本例中是构造函数参数,用于其 namedAttribute.

的名称

选项 2:没有命名属性,通过检查参数名称

然而,一个更简单的选择是完全放弃命名属性,只根据构造函数参数的名称进行过滤:

container.RegisterConditional<IEnumerable<IStudent>>(
    container.Collection.CreateRegistration<IStudent>(
        typeof(Student1)),
    c => c.Consumer.Target.Name == "seniors");

container.RegisterConditional<IEnumerable<IStudent>>(
    container.Collection.CreateRegistration<IStudent>(
        typeof(Student2),
        typeof(Student3)),
    c => c.Consumer.Target.Name == "juniors");

此注册与选项 1 几乎相同,但现在我们根据参数的实际名称而不是其属性进行过滤。

选项 3:通过手动连接 StudentsController。

一个更简单的选择是从 Auto-Wiring StudentsController 恢复为手动接线,如下所示:

var seniors = container.Collection.Create<IStudent>(typeof(Student1));
var juniors = container.Collection.Create<IStudent>(typeof(Student2), typeof(Student3));
container.Register(() => new StudentController(seniors: seniors, juniors: juniors));

这里我们请求容器创建两个 collection 学生,并为 StudentsController 创建注册,其中两个 collection 都被注入。

请注意,在 Simple Injector 中 collection 是流 。这意味着对 Collection.Create 的调用 不会 创建学生实例,只是对将在迭代时生成学生实例的流的引用。这意味着,可以在应用程序启动时创建流,同时保留注册生活方式。

另请注意,如果您调用 RegisterMvcControllers,则必须覆盖此控制器的现有注册 class。 This page 展示了如何执行此操作。

选项 4:更改设计。使 'Level' 成为 IStudent 的 属性。控制器内部过滤器。

您可以决定以简化注册的方式更改您的设计。这是否更好在很大程度上取决于上下文,所以请对此持保留态度。但是,当你在表示一个学生是否是学长的IStudent接口中添加一个Level属性,或者类似的东西,并根据里面的过滤 管理员,您的注册已减少到以下内容:

container.Collection.Register<IStudent>(typeof(Student1).Assembly);

这里我们使用Auto-Registration搜索所有学生实现并将它们全部注册到一个collection。

在这种情况下,StudentController 将保留一个构造函数参数,但您显然将过滤掉 Composition Root 的责任移到了控制器中。

public class StudentController
{
    private readonly IEnumerable<IStudent> _students;

    public StudentController(IEnumerable<IStudent> students)
    {
        _students = students;
    }
}