Autofac 6中如何注册同时提供另一接口服务的装饰器?
你遇到的这个问题确实是Autofac 6中装饰器注册方式变化带来的小坑——之前的DecoratorService方式允许我们一次性给装饰器附加多个服务,但新的RegisterDecoratorAPI默认只关注目标装饰接口,直接链式调用.As<T>()会被忽略,分开注册又会创建独立实例。
要实现「同一个装饰器实例既是IResourceStorage的装饰器,又作为INetworkQueueMetrics服务」的效果,你可以通过先注册装饰器为额外服务,再用RegisterDecorator关联到目标接口的方式解决,具体步骤如下:
方法一:手动关联装饰器实例(最直观)
- 先给原始的
IResourceStorage实现注册一个命名服务,方便装饰器引用:
// 注册你的原始IResourceStorage实现,命名为"inner-storage" builder.RegisterType<YourActualResourceStorage>() .Named<IResourceStorage>("inner-storage");
- 注册装饰器类,同时声明它实现
IResourceStorage和INetworkQueueMetrics,并注入原始实例,标记为SingleInstance确保全局唯一:
builder.RegisterType<ResourceStorageParallelOperationsLimitDecorator>() .As<IResourceStorage>() .As<INetworkQueueMetrics>() .SingleInstance() .WithParameter( // 匹配构造函数中的IResourceStorage参数 (param, ctx) => param.ParameterType == typeof(IResourceStorage), // 解析命名的原始实例 (param, ctx) => ctx.ResolveNamed<IResourceStorage>("inner-storage") );
这种方式不需要依赖RegisterDecoratorAPI,直接通过手动注册实现装饰器逻辑,同时保证两个接口解析到同一个实例。
方法二:结合RegisterDecoratorAPI(更符合Autofac 6的风格)
如果你更倾向于使用RegisterDecorator来明确表达装饰关系,可以这样做:
- 先注册装饰器为
INetworkQueueMetrics,并标记为单例:
builder.RegisterType<ResourceStorageParallelOperationsLimitDecorator>() .As<INetworkQueueMetrics>() .SingleInstance();
- 使用
RegisterDecorator的回调重载,在创建装饰器时重用已注册的单例实例,并传入原始的IResourceStorage:
builder.RegisterDecorator<IResourceStorage>( (context, parameters, innerStorage) => context.Resolve<ResourceStorageParallelOperationsLimitDecorator>( new TypedParameter(typeof(IResourceStorage), innerStorage) ) );
这里的关键是:通过context.Resolve<ResourceStorageParallelOperationsLimitDecorator>()获取我们已经注册的单例实例,而不是让Autofac新建一个,这样无论是解析IResourceStorage还是INetworkQueueMetrics,拿到的都是同一个对象。
验证测试
用你提供的测试代码验证,两种方法都能通过断言:
[Test] public void INetworkQueueMetrics_resolves_to_the_same_instance_as_ResourceStorage() { var container = BuildContainer(); var resourceStorage = container.Resolve<IResourceStorage>(); var networkMetrics = container.Resolve<INetworkQueueMetrics>(); resourceStorage.Should().BeSameAs(networkMetrics); // 现在会通过 }
为什么之前的方法会失败?
当你分开调用RegisterDecorator和独立注册ResourceStorageParallelOperationsLimitDecorator时,Autofac会把这当成两个完全独立的注册:一个是装饰器链中的实例,另一个是单独的INetworkQueueMetrics服务实例,自然会创建两个不同的对象。而上面的方法通过共享同一个注册实例,解决了这个问题。
内容的提问来源于stack exchange,提问作者Modern Ronin

