如何用Autofac解决泛型事件处理器多实现?求助事件订阅建模
嘿,刚接触Autofac碰到问题啦?别慌,我帮你把这个事件订阅的模型搭起来,一步步来保证能跑通~
第一步:定义核心接口
首先得把基础的事件和处理器接口捋清楚,用泛型处理器能让每个投影器精准对应特定事件,避免混乱:
// 领域事件的标记接口,所有事件都要实现它 public interface IDomainEvent { } // 泛型事件处理器接口,约束为处理特定的IDomainEvent实现类 public interface IEventHandler<in TEvent> where TEvent : IDomainEvent { Task HandleAsync(TEvent domainEvent); }
第二步:实现示例事件与投影器
我们先写两个示例,方便后续测试:
示例1:用户注册事件及投影器
// 用户注册事件 public class UserRegisteredEvent : IDomainEvent { public Guid UserId { get; set; } public string Email { get; set; } } // 对应投影器(事件处理器) public class UserRegisteredProjector : IEventHandler<UserRegisteredEvent> { public async Task HandleAsync(UserRegisteredEvent domainEvent) { // 这里写你的业务逻辑,比如更新读模型、发送通知等 Console.WriteLine($"✅ 处理用户注册事件:{domainEvent.Email}"); await Task.CompletedTask; } }
示例2:订单创建事件及投影器
// 订单创建事件 public class OrderCreatedEvent : IDomainEvent { public Guid OrderId { get; set; } public decimal Amount { get; set; } } // 对应投影器 public class OrderCreatedProjector : IEventHandler<OrderCreatedEvent> { public async Task HandleAsync(OrderCreatedEvent domainEvent) { Console.WriteLine($"✅ 处理订单创建事件:订单ID {domainEvent.OrderId},金额 {domainEvent.Amount:C}"); await Task.CompletedTask; } }
第三步:改造Events控制器
原来的控制器直接注入IEnumerable<IEventHandler>不够灵活,我们改用Autofac的IComponentContext来动态解析对应事件的处理器,这样新增事件时不用修改控制器代码:
[ApiController] [Route("api/events")] public class EventsController : ControllerBase { private readonly IComponentContext _componentContext; // 注入Autofac的组件上下文,用于动态解析处理器 public EventsController(IComponentContext componentContext) { _componentContext = componentContext; } [HttpPost] public async Task<IActionResult> Post([FromBody] IDomainEvent domainEvent) { if (domainEvent == null) return BadRequest("事件内容不能为空"); // 根据事件类型获取对应的处理器接口类型 var handlerInterfaceType = typeof(IEventHandler<>).MakeGenericType(domainEvent.GetType()); // 解析该类型下的所有处理器 var handlers = _componentContext.Resolve(typeof(IEnumerable<>).MakeGenericType(handlerInterfaceType)); // 遍历执行每个处理器的HandleAsync方法 foreach (dynamic handler in handlers) { await handler.HandleAsync((dynamic)domainEvent); } return Ok("事件处理完成"); } }
第四步:配置Autofac依赖注入
在.NET 6+的Program.cs里,我们替换默认DI为Autofac,并自动注册所有投影器:
var builder = WebApplication.CreateBuilder(args); // 添加控制器服务 builder.Services.AddControllers(); // 启用Autofac作为服务提供器 builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()); builder.Host.ConfigureContainer<ContainerBuilder>(containerBuilder => { // 扫描当前程序集,自动注册所有实现了IEventHandler<>的类 containerBuilder.RegisterAssemblyTypes(typeof(Program).Assembly) .AsClosedTypesOf(typeof(IEventHandler<>)) .InstancePerDependency(); // 每次请求创建新实例,可根据需求改为InstancePerRequest等 }); var app = builder.Build(); app.MapControllers(); app.Run();
这里的AsClosedTypesOf是Autofac的关键特性,它会自动识别所有实现了IEventHandler<TEvent>闭合类型的类,并将它们注册为对应的接口服务。
第五步:处理JSON序列化的多态问题
因为客户端POST的是JSON,ASP.NET Core需要知道如何把JSON反序列化为具体的IDomainEvent实现类,我们需要配置JSON的多态支持:
builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.TypeInfoResolver = new DefaultJsonTypeInfoResolver { Modifiers = { typeInfo => { if (typeInfo.Type == typeof(IDomainEvent)) { // 添加所有IDomainEvent的实现类,告诉序列化器如何识别它们 typeInfo.PolymorphismOptions.DerivedTypes.Add( new JsonDerivedType(typeof(UserRegisteredEvent), nameof(UserRegisteredEvent))); typeInfo.PolymorphismOptions.DerivedTypes.Add( new JsonDerivedType(typeof(OrderCreatedEvent), nameof(OrderCreatedEvent))); // 后续新增事件时,在这里继续添加即可 } } } }; });
客户端POST时,需要在JSON中带上类型标识,比如:
{ "$type": "UserRegisteredEvent", "UserId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "Email": "john.doe@example.com" }
测试验证
启动API,用Postman或curl向/api/eventsPOST上面的JSON,控制台就会输出投影器的处理日志,说明事件已经被正确处理啦~
内容的提问来源于stack exchange,提问作者diegosasw
相关产品推荐
相关产品推荐

