简单注入器:寄存器条件与寄存器单例



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

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

我这样做是为了欧文注册:

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));用于蚕豆,如果您想知道我如何在此处注册ObjectFactory件,它是: 类:

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>的不同封闭版本:

  • 解析为根类型时会创建一个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>>().

这一切都意味着,使用实现类型工厂的RegisterConditionalSingleton保证(如示例中所示)仅适用于指定的封闭泛型类型。

请注意,RegisterConditional并不是简单注入器中唯一看到此行为的部分。以下注册具有相同的效果:

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

在这两种情况下,都可以创建Foo<T>FooDecorator<T>的封闭泛型版本的多个实例,但简单注入器保证每个封闭泛型版本的Foo<T>只有一个实例。然而,对于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.

此信息反映在以下简单喷油器文档部分中:

  • 面向方面的编程 - 将生活方式应用于装饰者
  • 生命周期管理 - 泛型和生命周期管理

最新更新