Autofac 通用装饰器未被触发(使用 mediatr)
Autofac generic decorator not fired (with mediatr)
我在使用我的通用装饰器时遇到了问题,并且找不到关于我所查看的任何主题的解决方案。我想我很接近,但我错过了一些东西。
为了简化,我在 .NetCore 中有一个 API,在 .Net Standard 中有一个模块。我想在 .Net Standard 中管理模块的 DI,API 只需在 Startup.
中调用它
在Program.cs中,我使用autofac服务提供者工厂
public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => webBuilder
.UseStartup<Startup>()
.UseSerilog()
)
.UseServiceProviderFactory(new AutofacServiceProviderFactory());
}
在我的 Startup.cs 中,我有 ConfigureContainer 初始化我的模块
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterModule(new MyModuleAutofacModule());
MyModuleStartup.Initialize();
}
在我的模块启动中,我注册了我的子模块、类型...
public static void Initialize()
{
var containerBuilder = new ContainerBuilder();
containerBuilder.AddMediatR(typeof(StudiDropboxModule).Assembly);
containerBuilder.RegisterModule(new ProcessingModule());
...
containerBuilder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.Where(t => t.Name.EndsWith("Handler"))
.AsImplementedInterfaces();
containerBuilder.RegisterGenericDecorator(
typeof(LoggingCommandHandlerDecorator<>),
typeof(ICommandHandler<>));
}
Command/Command 处理程序继承自 Mediatr
public interface ICommand : IRequest
{
}
public interface ICommandHandler<in TCommand> : IRequestHandler<TCommand> where TCommand : ICommand
{
}
我的Command/Command处理程序
public class ExampleCommand : ICommand
{
...
}
internal class ExampleCommandHandler : ICommandHandler<ExampleCommand>
{
public async Task<Unit> Handle(ExampleCommand request, CancellationToken cancellationToken)
{
...
return Unit.Value;
}
}
装饰者
internal class LoggingCommandHandlerDecorator<T> : ICommandHandler<T> where T : ICommand
{
private readonly ICommandHandler<T> _decorated;
public LoggingCommandHandlerDecorator(ICommandHandler<T> decorated)
{
_decorated = decorated;
}
public async Task<Unit> Handle(T command, CancellationToken cancellationToken)
{
var result = await _decorated.Handle(command, cancellationToken);
return result;
}
}
当我用 mediatr 调用我的命令时,装饰器没有被触发。
当我调用命令时 Mediatr 已解决
internal static async Task Execute(ICommand command)
{
using (var scope = MyModuleCompositionRoot.BeginLifetimeScope())
{
var mediator = scope.Resolve<IMediator>();
await mediator.Send(command);
}
}
我想我错过了 autofac 的一些东西。
提前致谢。
MediatR 使用 IRequestHandler
抽象。这意味着 IMediator
实现在内部要求容器提供特定的 IRequestHandler<T>
。
另一方面,您从 IRequestHandler<T>
派生了一个 ICommandHandler<T>
,并在此基础上实现了装饰器。然而,Autofac(以及大多数 DI 容器)将无法将请求的 IRequestHandler<T>
包装在另一个接口的装饰器中。这与 Autofac 的设置方式有关。
要解决您的问题,有以下几种解决方案:
- 删除自定义
ICommandHandler<T>
抽象。它似乎毫无用处,只会使您的申请复杂化。
- 将默认的
IMediator
实现替换为解析 ICommandHandler<T>
抽象的实现。
- 创建一个(通用的)
IRequestHandler<T>
代理,将调用转发到包装的 ICommandHandler<T>
。
- 完全放弃 MediatR。
我遇到了同样的问题。
真的很难弄清楚如何在 MediatR 和 Autofac v5 和 v6 中注册装饰器。
所以我创建了一个示例存储库,它描述了如何在最新版本的 Autofac 和 MediatR 中实现清晰的请求、命令和查询处理:github.com/LeftTwixWand/ModernCQRS
示例输出:
我在使用我的通用装饰器时遇到了问题,并且找不到关于我所查看的任何主题的解决方案。我想我很接近,但我错过了一些东西。
为了简化,我在 .NetCore 中有一个 API,在 .Net Standard 中有一个模块。我想在 .Net Standard 中管理模块的 DI,API 只需在 Startup.
中调用它在Program.cs中,我使用autofac服务提供者工厂
public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => webBuilder
.UseStartup<Startup>()
.UseSerilog()
)
.UseServiceProviderFactory(new AutofacServiceProviderFactory());
}
在我的 Startup.cs 中,我有 ConfigureContainer 初始化我的模块
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterModule(new MyModuleAutofacModule());
MyModuleStartup.Initialize();
}
在我的模块启动中,我注册了我的子模块、类型...
public static void Initialize()
{
var containerBuilder = new ContainerBuilder();
containerBuilder.AddMediatR(typeof(StudiDropboxModule).Assembly);
containerBuilder.RegisterModule(new ProcessingModule());
...
containerBuilder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.Where(t => t.Name.EndsWith("Handler"))
.AsImplementedInterfaces();
containerBuilder.RegisterGenericDecorator(
typeof(LoggingCommandHandlerDecorator<>),
typeof(ICommandHandler<>));
}
Command/Command 处理程序继承自 Mediatr
public interface ICommand : IRequest
{
}
public interface ICommandHandler<in TCommand> : IRequestHandler<TCommand> where TCommand : ICommand
{
}
我的Command/Command处理程序
public class ExampleCommand : ICommand
{
...
}
internal class ExampleCommandHandler : ICommandHandler<ExampleCommand>
{
public async Task<Unit> Handle(ExampleCommand request, CancellationToken cancellationToken)
{
...
return Unit.Value;
}
}
装饰者
internal class LoggingCommandHandlerDecorator<T> : ICommandHandler<T> where T : ICommand
{
private readonly ICommandHandler<T> _decorated;
public LoggingCommandHandlerDecorator(ICommandHandler<T> decorated)
{
_decorated = decorated;
}
public async Task<Unit> Handle(T command, CancellationToken cancellationToken)
{
var result = await _decorated.Handle(command, cancellationToken);
return result;
}
}
当我用 mediatr 调用我的命令时,装饰器没有被触发。
当我调用命令时 Mediatr 已解决
internal static async Task Execute(ICommand command)
{
using (var scope = MyModuleCompositionRoot.BeginLifetimeScope())
{
var mediator = scope.Resolve<IMediator>();
await mediator.Send(command);
}
}
我想我错过了 autofac 的一些东西。
提前致谢。
MediatR 使用 IRequestHandler
抽象。这意味着 IMediator
实现在内部要求容器提供特定的 IRequestHandler<T>
。
另一方面,您从 IRequestHandler<T>
派生了一个 ICommandHandler<T>
,并在此基础上实现了装饰器。然而,Autofac(以及大多数 DI 容器)将无法将请求的 IRequestHandler<T>
包装在另一个接口的装饰器中。这与 Autofac 的设置方式有关。
要解决您的问题,有以下几种解决方案:
- 删除自定义
ICommandHandler<T>
抽象。它似乎毫无用处,只会使您的申请复杂化。 - 将默认的
IMediator
实现替换为解析ICommandHandler<T>
抽象的实现。 - 创建一个(通用的)
IRequestHandler<T>
代理,将调用转发到包装的ICommandHandler<T>
。 - 完全放弃 MediatR。
我遇到了同样的问题。
真的很难弄清楚如何在 MediatR 和 Autofac v5 和 v6 中注册装饰器。
所以我创建了一个示例存储库,它描述了如何在最新版本的 Autofac 和 MediatR 中实现清晰的请求、命令和查询处理:github.com/LeftTwixWand/ModernCQRS
示例输出: