如何使用Autofac实现责任链设计模式

问题描述 投票:0回答:1

我正在尝试使用 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();
    }
}
c# .net autofac
1个回答
0
投票

我认为如果没有一些帮助代码,您将很难自动连接它。问题是责任链就是......

  • 顺序:您必须知道处理程序的顺序。
  • 相同的界面:
    IHandler
    调用
    IHandler
    调用
    IHandler

Autofac 将按注册顺序解析

IHandlers
中已注册的
IEnumerable<IHandler>
,但不存在“如果我要解析列表中的第 12 个
IHandler
,那么如果我请求
IHandler
构造函数参数”的概念我真的想要名单上的第 13 个。”如果您要求
IHandler
(单个实例,而不是
IEnumerable<IHandler>
),您将总是 会获得最后一个注册。

这就是为什么整个“相同的界面”是一个问题。

你能做的最好的事情就是:

  • 按顺序注册
    IHandler
    实例。
  • 帮助代码...
    • 解决
      IEnumerable<IHandler>
      (这些将被订购)。
    • Foreach 处理程序并执行类似
      currentHandler.SetNext(nextHandler);
      的操作 - 您不会能够将下一个处理程序注入到构造函数中。

但这并不是 Autofac 只是神奇地将它们连接在一起 - 这是你在做的。这与您自己创建一个包含这些内容的数组,然后手动对其进行 foreach 并将其连接起来没有太大区别。

我建议不要指望 Autofac 来实现这个魔法,而是看看 .NET Core 中的中间件是如何工作的。这就是让您可以使用处理程序构建应用程序的东西,因为 ASP.NET 中的中间件正是这种模式。

© www.soinside.com 2019 - 2024. All rights reserved.