简单注入器: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
创建的,而多个实例是用 RegisterConditional
和 Lifestyle.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
只能依赖于 Logger1
或 Logger2
,而不是两者,所以简单注入器将不得不为每个元素创建一个新的 LoggerDecorator
实例。如果 LoggerDecorator
将被缓存为真正的单例,这将是结果:
private static ILogger logger = new LoggerDecorator(new Logger1());
private static loggers = new[] { logger, logger };
因为只有 1 个 LoggerDecorator
,这个装饰器依赖于 Logger1
,这意味着记录器集合只有两个元素都指向同一个 Logger1
实例.没有Logger2
.
此信息反映在以下 Simple Injector 文档部分中:
上下文:我正在尝试使用 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
创建的,而多个实例是用 RegisterConditional
和 Lifestyle.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
只能依赖于 Logger1
或 Logger2
,而不是两者,所以简单注入器将不得不为每个元素创建一个新的 LoggerDecorator
实例。如果 LoggerDecorator
将被缓存为真正的单例,这将是结果:
private static ILogger logger = new LoggerDecorator(new Logger1());
private static loggers = new[] { logger, logger };
因为只有 1 个 LoggerDecorator
,这个装饰器依赖于 Logger1
,这意味着记录器集合只有两个元素都指向同一个 Logger1
实例.没有Logger2
.
此信息反映在以下 Simple Injector 文档部分中: