我正在尝试使用 Autofac 来实现一个责任链设计模式来连接所有内容以进行依赖注入。当我运行示例项目时,我从 Autofac 收到一条消息,表明该组件尚未注册。
我知道我可以使用 RegisterDecorator 设置这个特定的示例,但这并不总是适用于责任链。如果我需要使用 SetNextHandler 方法,我将如何连接它?对于这种情况,有比 RegisterDecorator 更好的东西吗?
下面是我用来重现错误的示例代码。 Autofac 注册是 ChatGPT 建议连接的几种方法之一。
IHandler
namespace ChainOfResponsibilitySample;
internal interface IHandler {
void Handle();
}
ConcreteHandlerA
namespace ChainOfResponsibilitySample;
internal class ConcreteHandlerA : IHandler {
private readonly IHandler _next;
public ConcreteHandlerA(IHandler next) {
_next = next;
}
public void Handle() {
// do something
_next.Handle();
}
}
ConcreteHandlerB
namespace ChainOfResponsibilitySample;
internal class ConcreteHandlerB : IHandler {
private readonly IHandler _next;
public ConcreteHandlerA(IHandler next) {
_next = next;
}
public void Handle() {
// do something
_next.Handle();
}
}
ConcreteHandlerC
namespace ChainOfResponsibilitySample;
internal class ConcreteHandlerC : IHandler {
public void Handle() {
// do something
}
}
Autofac模块
using Autofac;
namespace ChainOfResponsibilitySample;
internal class AutofacModule : Module {
protected override void Load(ContainerBuilder builder) {
builder.RegisterType<ConcreteHandlerC>()
.AsImplementedInterfaces();
builder.RegisterType<ConcreteHandlerB>()
.AsImplementedInterfaces()
.WithParameter((pi, ctx) => pi.ParameterType == typeof(IHandler),
(pi, ctx) => ctx.Resolve<IHandler>());
builder.RegisterType<ConcreteHandlerA>()
.AsImplementedInterfaces()
.WithParameter((pi, ctx) => pi.ParameterType == typeof(IHandler),
(pi, ctx) => ctx.Resolve<IHandler>());
base.Load(builder);
}
}
主逻辑
using Microsoft.Extensions.Hosting;
namespace ChainOfResponsibilitySample;
internal class MainLogic : IHostedService {
private readonly IHandler _handler;
public MainLogic(IHandler handler) {
_handler = handler;
}
public Task StartAsync(CancellationToken cancellationToken) {
return Task.Run(() => _handler.Handle(), cancellationToken);
}
public Task StopAsync(CancellationToken cancellationToken) {
return Task.CompletedTask;
}
}
程序.cs
using Autofac;
using Autofac.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace ChainOfResponsibilitySample;
public class Program {
public static void Main(string[] args) {
Host.CreateDefaultBuilder()
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureContainer<ContainerBuilder>(cb => cb.RegisterModule<AutofacModule>())
.ConfigureServices(services => {
services.AddHostedService<MainLogic>();
})
.Build()
.Run();
}
}
我认为如果没有一些帮助代码,您将很难自动连接它。问题是责任链就是......
IHandler
调用IHandler
调用IHandler
。Autofac 将按注册顺序解析
IHandlers
中已注册的 IEnumerable<IHandler>
,但不存在“如果我要解析列表中的第 12 个 IHandler
,那么如果我请求 IHandler
构造函数参数”的概念我真的想要名单上的第 13 个。”如果您要求 IHandler
(单个实例,而不是 IEnumerable<IHandler>
),您将总是 会获得最后一个注册。
这就是为什么整个“相同的界面”是一个问题。
你能做的最好的事情就是:
IHandler
实例。IEnumerable<IHandler>
(这些将被订购)。currentHandler.SetNext(nextHandler);
的操作 - 您不会能够将下一个处理程序注入到构造函数中。但这并不是 Autofac 只是神奇地将它们连接在一起 - 这是你在做的。这与您自己创建一个包含这些内容的数组,然后手动对其进行 foreach 并将其连接起来没有太大区别。
我建议不要指望 Autofac 来实现这个魔法,而是看看 .NET Core 中的中间件是如何工作的。这就是让您可以使用处理程序构建应用程序的东西,因为 ASP.NET 中的中间件正是这种模式。