简单注入器:Register Conditional vs RegisterSingleton

Simple Injector: Register Conditional vs RegisterSingleton

上下文:我正在尝试使用 serilog 和弹性搜索接收器在我们的 api 中捕获多个事件,这些事件包括:GET 操作(常规网络 api 流程)以及登录尝试(欧文)。 我正在这样注册 serilog:

container.RegisterConditional(
    typeof(ICustomLogger),
    c => typeof(CustomLogger<>).MakeGenericType(
        c.Consumer?.ImplementationType ?? typeof(object)),
    Lifestyle.Singleton,
    c => true);

我这样做是为了 Owin 注册:

app.Use(async (context, next) =>
{
    using (var scope = container.BeginExecutionContextScope())
    {
        await next.Invoke();
    }
});

然后当我调用 container.Verify(); 时,记录器构造函数被调用(如预期的那样)。 但是,当我从我的 OAuthAuthorizationServerProvider 实现中调用记录器时,如下所示: var logger = ObjectFactory.GetInstance<ICustomLogger>(); 构造函数再次被调用,这意味着单例不再是真正的单例,我完全了解服务定位器反模式(现在真的没有太多时间重构那部分),有趣的是如果我将记录器注册更改为以下内容(去掉类型部分),那么只会创建一个实例:

container.RegisterSingleton(typeof(ICustomLogger), typeof(CustomLogger))

我尝试使用第一个选项的原因是因为我希望能够将 .ForContext(typeof(T)); 用于 serilog,如果您想知道我如何注册 ObjectFactory 片段这里是: Class:

public static class ObjectFactory
{
    public static Container container;

    public static void SetContainer(Container container)
    {
        ObjectFactory.container = container;
    }

    public static T GetInstance<T>() where T : class
    {
        return container.GetInstance<T>();
    }
}

注册(来自我的主引导程序):

ObjectFactory.SetContainer(container);

所以我的主要问题是:为什么只有一个实例是用 RegisterSingleton 创建的,而多个实例是用 RegisterConditionalLifestyle.Singleton 创建的?

创建多个实例的原因与您使用的 ObjectFactory 无关,而仅仅是因为创建了 CustomLogger<T> 的不同封闭版本:

  • A CustomLogger<object> 在解析为根类型时创建(通过调用 GetInstance<ICustomLogger>()
  • ICustomLogger 被注入 Service1 时创建一个 CustomLogger<Service1>

因为 CustomLogger<object>CustomLogger<Service1> 不同的类型,不可能两种类型都只有一个实例。它们都必须被创建。这可能看起来很奇怪,但请考虑这两个 类:

public class Service1
{
    public Service1(ICustomLogger logger) { }
}

public class Service2
{
    public Service2(ICustomLogger logger) { }
}

在运行时,考虑到这两个定义,您想创建类似于以下的对象图:

var s1 = new Service1(new CustomLogger<Service1>());
var s2 = new Service2(new CustomLogger<Service2>());

在上述情况下,CustomLogger 未被缓存,因此实​​际上是瞬态的。您可能会尝试将其重写为以下内容,但不会相同:

ICustomLogger logger = new CustomLogger<Service1>();
var s1 = new Service1(logger);
var s2 = new Service2(logger);

现在两个服务都获得相同的单个实例。但是,这意味着 Service2 得到的 CustomLogger<Service1> 不是您配置的。您将依赖项配置为:

typeof(CustomLogger<>).MakeGenericType(c.Consumer.ImplementationType)

这样做的结果是,当 Service2 开始记录时,消息看起来像是来自 Service1。这可能是不正确的行为。否则,您可以简单地调用 RegisterSingleton<ICustomLogger, CustomLogger<Service1>>().

这一切意味着 Singleton 对使用实现类型工厂(如您的示例)的 RegisterConditional 的保证仅适用于指定的封闭泛型.

请注意,RegisterConditional 并不是 Simple Injector 中您看到此行为的唯一部分。以下注册确实具有相同的效果:

container.RegisterSingleton(typeof(IFoo<>), typeof(Foo<>));

container.RegisterDecorator(typeof(IFoo<>), typeof(FooDecorator<>), Lifestyle.Singleton);

在这两种情况下,可以创建 Foo<T>FooDecorator<T> 的封闭泛型版本的多个实例,但是 Simple Injector 保证 [= 的每个封闭泛型版本只有一个实例34=]。但是,对于 RegisterDecorator<T>,即使这样也不能保证。考虑这个例子:

container.Collection.Append<ILogger, Logger1>(Lifestyle.Singleton);
container.Collection.Append<ILogger, Logger2>(Lifestyle.Singleton);

container.RegisterDecorator<ILogger, LoggerDecorator>(Lifestyle.Singleton);

在这种情况下,注册了 ILogger 个组件的集合,其中每个 ILogger 元素将用 LoggerDecorator 包装。因为 LoggerDecorator 只能依赖于 Logger1Logger2,而不是两者,所以简单注入器将不得不为每个元素创建一个新的 LoggerDecorator 实例。如果 LoggerDecorator 将被缓存为真正的单例,这将是结果:

private static ILogger logger = new LoggerDecorator(new Logger1());

private static loggers = new[] { logger, logger };

因为只有 1 个 LoggerDecorator,这个装饰器依赖于 Logger1,这意味着记录器集合只有两个元素都指向同一个 Logger1 实例.没有Logger2.

此信息反映在以下 Simple Injector 文档部分中: