上下文:我正在尝试使用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
创建的,而是用RegisterConditional
和Lifestyle.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>>()
.
这一切都意味着,使用实现类型工厂的RegisterConditional
的Singleton保证(如示例中所示)仅适用于指定的封闭泛型类型。
请注意,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
只能依赖于Logger1
或Logger2
,而不是两者,所以简单注入器必须为每个元素创建一个新的LoggerDecorator
实例。如果LoggerDecorator
缓存为真正的单例,则结果如下:
private static ILogger logger = new LoggerDecorator(new Logger1());
private static loggers = new[] { logger, logger };
由于只有 1 个LoggerDecorator
,这个装饰器依赖于Logger1
,这意味着记录器的集合只有两个元素都指向同一个Logger1
实例。没有Logger2
.
此信息反映在以下简单喷油器文档部分中:
- 面向方面的编程 - 将生活方式应用于装饰者
- 生命周期管理 - 泛型和生命周期管理