在 Simple Injector 中注册时设置收集项目的 Lifestyle

Setting Lifestyle of collection items during registration in Simple Injector

Container.Collection.Register 似乎没有需要 Lifestyle 的重载。所有发现的实现都将注册为默认 Lifestyle。省略这种过载的原因是什么?

添加一个集合的首选方法是什么,其中所有项目都应该有一个不是默认生活方式的 Lifestyle

What is the reasoning behind the omission of such an overload?

首先,因为,(如Eric Lippert stated):

no one ever designed, specified, implemented, tested, documented and shipped that feature. All six of those things are necessary to make a feature happen. All of them cost

其次,接受 Type 实例列表的 Collection.Register 重载(例如 Collection.Register<TService>(params Type[])),接受实现和抽象的列表。为抽象提供 Lifestyle 没有多大意义,甚至令人困惑。

要了解为什么 Simple Injector 允许提供抽象,请查看以下注册:

container.Collection.Register<ILogger>(
    typeof(ILogger),
    typeof(ISpecialLogger),
    typeof(SqlLogger));

container.Register<ILogger, DefaultLogger>(Lifestyle.Scoped);
container.Register<ISpecialLogger, SpecialLogger>(Lifestyle.Singleton);

在这个例子中,注册了一个记录器集合,其中两个提供的类型是抽象的。允许提供抽象背后的想法是,有了它,您可以将它们指向其他注册。这正是前面示例所做的。解析记录器集合时,它将由 Scoped DefaultLoggerSingleton SpecialLoggerTransient SqlLogger.

现在考虑一个假设的新 Collection.Register 重载,它接受 Lifestyle:

container.Collection.Register<ILogger>(new[]
    {
        typeof(ILogger),
        typeof(ISpecialLogger),
        typeof(SqlLogger)
    },
    Lifestyle.Transient);

container.Register<ILogger, DefaultLogger>(Lifestyle.Scoped);
container.Register<ISpecialLogger, SpecialLogger>(Lifestyle.Singleton);

所有元素都是Transient,而其中两个元素指向不同生活方式的注册是什么意思?

我们还没有找到解决这些问题的好方法 API(目前),这就是为什么容器没有这样的重载。

What is the preferred way of adding a collection where all items should have a Lifestyle that is not the default lifestyle?

有多种方法可以做到这一点。

选项 1:显式注册元素

在 Simple Injector 中注册的集合使用回退机制来确定它们的生活方式。这是通过检查是否存在集合元素的具体注册来完成的。例如:

container.Collection.Register<ILogger>(typeof(SqlLogger), typeof(FileLogger));

// Ensures that SqlLogger is a Singleton when part of the IEnumerable<ILogger>.
container.Register<SqlLogger>(Lifestyle.Singleton);

当 Simple Injector 首次解析 IEnumerable<ILogger> 时,对于每个元素它将(按以下顺序):

  • 尝试获取显式注册的具体注册(示例中:Register<SqlLogger>
  • 尝试使用未注册的类型解析获取该注册
  • 尝试在使用配置的 Container.Options.LifestyleSelectionBehavior(默认为 Transient)的同时自行创建注册。

选项 2:使用 Register(Type, IEnumerable) 重载

除了向 Collection.Register 提供类型或程序集的列表之外,您还可以提供 Registration 实例的列表。 Registration 描述了为特定生活方式创建特定组件,当您调用 Container.RegisterContainer.Collection.Register 时,简单注入在内部使用此 class。但您也可以手动创建 Registration 个实例并将它们提供给 Collection.Register 重载,如下所示:

// Load the list of types without registering them
Type[] types = container.GetTypesToRegister<ILogger>(assemblies);

// Register them using the overload that takes in a list of Registrations
container.Collection.Register<ILogger>(
    from type in types
    select Lifestyle.Transient.CreateRegistration(type, container));

这会强制集合的所有注册类型都具有 Transient 生活方式。如果需要,您还可以为每种类型赋予其特定的生活方式。

选项 3:覆盖 LifestyleSelectionBehavior

当找不到注册时,Collection.Register 会在最后一分钟使用配置的 LifestyleSelectionBehavior 自行注册。默认选择行为总是 returns Lifestyle.Transient,但可以更改此行为。例如:

class CustomLifestyleSelectionBehavior : ILifestyleSelectionBehavior
{
    public Lifestyle SelectLifestyle(Type implementationType) =>
        implementationType == typeof(SqlLogger)
            ? Lifestyle.Singleton
            : Lifestyle.Transient;
}

这个实现显然有点天真,但展示了这个概念。您可以按如下方式覆盖默认行为:

container.Options.LifestyleSelectionBehavior =
    new CustomLifestyleSelectionBehavior();

选项 4:追加

Collection.Register之后,可以一次注册所有元素,您可以使用Collection.Append方法。它们允许 one-by-one 注册集合元素:

container.Collection.Append<ILogger, SqlLogger>(Lifestyle.Singleton);
container.Collection.Append<ILogger, FileLogger>(Lifestyle.Transient);

还有 non-generic 重载可用,它简化了 auto-registering 这些类型,例如当使用 Container.GetTypesToRegister.

返回时

这些是您在 Simple Injector v4.6 中的基本选项。我们可能会决定在将来添加一个方便的 Collection.Register<T>(IEnumerable<Type>, Lifestyle) 重载,因为这个重载的省略确实会不时引起一些混乱。