缓存依赖项,还是在注入时复制它们?
Caching dependencies, or copy them at injection?
我有一些由 Autofac 初始化的配置对象。
它们的负载很重(读取配置文件、反序列化、验证一些东西、映射匹配的 POCO 对象...)。
但是性能很好,因为注册的对象被声明为.SingleInstance()
,所以它只加载一次,第一次被注入到某个地方,之后总是使用同一个实例。
builder.Register(c => _settingsReader.LoadSection(thing)) // Read and map object
.OnActivated(t =>
{
// Do some checks on it to see if settings appear to be correct
})
.As(settingInterface)
.SingleInstance();
但是,这些对象可以稍后修改(有意或无意),并且由于它们是单例,如果发生这种情况,整个应用程序都会受到影响。
能够更新初始单例本身(以全局覆盖定义的设置)和更新本地副本(以更改已注入设置 class 的对象的行为,但不是整个应用程序)也会很有趣。
我考虑了两种可能:
- 要求 Autofac 注入对象的“副本”,而不是相同的实例,但我认为其中没有这样的“缓存/复制”功能,我会失去操作单例的能力,或
- 在注入对象时使用Automapper深拷贝对象,并将拷贝存储在依赖对象中而不是原始对象中。我对 automapper 很好(而且对象很轻,所以性能不会成为问题),但是为每个注入的副本添加副本 属性 会增加我的 Autofac 配置或我的对象本身的负担,具体取决于我放置的位置“复制此对象”代码。
要使用 Automapper 的想法,我必须做类似的事情:
builder.RegisterType<MyConsumingClass>()
.WithParameter(
new ResolvedParameter(
(pi, ctx) => pi.ParameterType == typeof(IMySettingClass),
(pi, ctx) =>
{
var mapper = ctx.Resolve<Mapper>();
return mapper.Map<ConcreteSettingClass>(ctx.Resolve<IMySettingClass>());
}))
.As<IMyConsumingClass>();
而不只是
builder.RegisterType<MyConsumingClass>().As<IMyConsumingClass>();
(之前的代码没有测试,只是为了举例)
如果能就处理此问题的最佳方法提出建议,我将不胜感激。
如评论中所述,由于缺乏其他建议,我最终保留了自己的初始方法,并在创建消费 class 的新实例时使用 Automapper 克隆我的设置对象。
这是服务及其设置对象的示例。
如果您的“设置”对象在其属性中有 classes,请记住也要克隆它们,否则您将只有初始单例的参考副本!为了避免这个陷阱,您还可以使用比 Automapper 更适合这种情况的真正的“克隆库”。
public class ServiceFacadeModule
: Module
{
protected override void Load(ContainerBuilder builder)
{
// Modifying the settings on the service instance itself should NOT affect global setting object. To achieve this, settings classes are copied when initializing new service instances
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<ServiceSettings, ServiceSettings>();
});
var mapper = new Mapper(config);
builder.RegisterType<ServiceDAO>()
.WithParameter(
new ResolvedParameter(
(pi, _) => pi.ParameterType == typeof(ISettings),
(_, ctx) => mapper.Map<ServiceSettings>(ctx.Resolve<ISettings>())))
.As<IServiceDAO>()
.InstancePerLifetimeScope();
}
}
我有一些由 Autofac 初始化的配置对象。
它们的负载很重(读取配置文件、反序列化、验证一些东西、映射匹配的 POCO 对象...)。
但是性能很好,因为注册的对象被声明为.SingleInstance()
,所以它只加载一次,第一次被注入到某个地方,之后总是使用同一个实例。
builder.Register(c => _settingsReader.LoadSection(thing)) // Read and map object
.OnActivated(t =>
{
// Do some checks on it to see if settings appear to be correct
})
.As(settingInterface)
.SingleInstance();
但是,这些对象可以稍后修改(有意或无意),并且由于它们是单例,如果发生这种情况,整个应用程序都会受到影响。
能够更新初始单例本身(以全局覆盖定义的设置)和更新本地副本(以更改已注入设置 class 的对象的行为,但不是整个应用程序)也会很有趣。
我考虑了两种可能:
- 要求 Autofac 注入对象的“副本”,而不是相同的实例,但我认为其中没有这样的“缓存/复制”功能,我会失去操作单例的能力,或
- 在注入对象时使用Automapper深拷贝对象,并将拷贝存储在依赖对象中而不是原始对象中。我对 automapper 很好(而且对象很轻,所以性能不会成为问题),但是为每个注入的副本添加副本 属性 会增加我的 Autofac 配置或我的对象本身的负担,具体取决于我放置的位置“复制此对象”代码。
要使用 Automapper 的想法,我必须做类似的事情:
builder.RegisterType<MyConsumingClass>()
.WithParameter(
new ResolvedParameter(
(pi, ctx) => pi.ParameterType == typeof(IMySettingClass),
(pi, ctx) =>
{
var mapper = ctx.Resolve<Mapper>();
return mapper.Map<ConcreteSettingClass>(ctx.Resolve<IMySettingClass>());
}))
.As<IMyConsumingClass>();
而不只是
builder.RegisterType<MyConsumingClass>().As<IMyConsumingClass>();
(之前的代码没有测试,只是为了举例)
如果能就处理此问题的最佳方法提出建议,我将不胜感激。
如评论中所述,由于缺乏其他建议,我最终保留了自己的初始方法,并在创建消费 class 的新实例时使用 Automapper 克隆我的设置对象。 这是服务及其设置对象的示例。 如果您的“设置”对象在其属性中有 classes,请记住也要克隆它们,否则您将只有初始单例的参考副本!为了避免这个陷阱,您还可以使用比 Automapper 更适合这种情况的真正的“克隆库”。
public class ServiceFacadeModule
: Module
{
protected override void Load(ContainerBuilder builder)
{
// Modifying the settings on the service instance itself should NOT affect global setting object. To achieve this, settings classes are copied when initializing new service instances
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<ServiceSettings, ServiceSettings>();
});
var mapper = new Mapper(config);
builder.RegisterType<ServiceDAO>()
.WithParameter(
new ResolvedParameter(
(pi, _) => pi.ParameterType == typeof(ISettings),
(_, ctx) => mapper.Map<ServiceSettings>(ctx.Resolve<ISettings>())))
.As<IServiceDAO>()
.InstancePerLifetimeScope();
}
}