在MassTransit中配置请求响应时遭遇依赖注入异常
问题描述
在Token微服务中基于MassTransit配置请求/响应模式,调用User微服务获取响应时,启动阶段触发System.AggregateException,核心异常信息为:
激活TokenService时无法解析MassTransit.IRequestClient`1[Dodis.Domain.Core.Messages.Requests.GetUserRequest]服务
相关代码
Token微服务依赖容器代码
public static void RegisterServices(IServiceCollection services, string MicroService, IConfiguration configuration) { //services.AddTransient<ITokenService, TokenService>(); var tokenSettings = new TokenSettings(); configuration.Bind("Token", tokenSettings); services.Configure<TokenSettings>(configuration.GetSection("Token")); #region MassTransit try { services.AddMassTransit(config => { config.SetKebabCaseEndpointNameFormatter(); config.AddRequestClient<GetUserRequest>(); config.UsingRabbitMq((context, configurator) => { configurator.Host("localhost", "/", h => { h.Username("guest"); h.Password("guest"); }); configurator.ConfigureEndpoints(context); }); }); } catch (Exception ex) { throw new Exception(ex.Message, ex); } #endregion try { services.AddScoped<ITokenService>(sp => { var tokenFactory = sp.GetRequiredService<ITokenFactory>(); var tokenRepository = sp.GetRequiredService<ITokenRepository>(); var requestClient = sp.GetRequiredService<IRequestClient<GetUserRequest>>(); return new TokenService(tokenFactory, tokenRepository, requestClient); }); } catch (Exception ex) { throw new Exception(ex.Message, ex); } services.AddDbContext<TokenDbContext>(options => { options.UseSqlServer(hiding this); }); services.AddScoped<ITokenRepository, TokenRepository>(); }
TokenService代码
public class TokenService : ITokenService { private readonly ITokenFactory _tokenFactory; private readonly ITokenRepository _repository; private readonly IRequestClient<GetUserRequest> _client; public TokenService(ITokenFactory tokenFactory, ITokenRepository repository, IRequestClient<GetUserRequest> client) { _tokenFactory = tokenFactory; _repository = repository; _client = client; } public async Task<AccessResult> CreateToken(TokenRequest model) { var existingToken = await _repository.GetTokenByUserIdAndProvider(model.UserId, "Dodis", "Access_Token"); if (existingToken != null) { existingToken.Value = await _tokenFactory.CreateToken(new TokenRequest { UserId = model.UserId, RoleName = model.RoleName, }); existingToken.ExpireDate = DateTime.UtcNow.AddMinutes(20160); await _repository.UpdateToken(existingToken); var response = await _client.GetResponse<GetUserResponse>(new GetUserRequest { Id = model.UserId }); // ... 剩余代码 } // ... 剩余代码 } }
User微服务依赖容器代码
public static void RegisterServices(IServiceCollection services, string MicroService, IConfiguration? configuration = null) { //Mediatr services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly())); // MassTransit try { services.AddMassTransit(config => { //// Add the request client //config.AddRequestClient<GetUserRequest>(); config.SetKebabCaseEndpointNameFormatter(); config.AddConsumer<GetUserConsumer>(); config.UsingRabbitMq((context, configurator) => { configurator.Host("rabbitmq://localhost", h => { h.Username("guest"); h.Password("guest"); }); configurator.ConfigureEndpoints(context); }); }); } catch (Exception ex) { throw new Exception(ex.Message, ex); } }
排查原因及解决方案
1. 缺少MassTransit宿主服务注册
问题:Token微服务仅调用了services.AddMassTransit(...)配置总线,但未注册MassTransit的宿主服务,导致总线(IBus)未完成初始化,而IRequestClient<GetUserRequest>依赖正常运行的总线实例,因此无法被容器解析。
解决方案:在Token微服务的MassTransit配置代码块后,添加宿主服务注册:
services.AddMassTransitHostedService();
修改后的MassTransit配置部分:
try { services.AddMassTransit(config => { config.SetKebabCaseEndpointNameFormatter(); config.AddRequestClient<GetUserRequest>(); config.UsingRabbitMq((context, configurator) => { configurator.Host("localhost", "/", h => { h.Username("guest"); h.Password("guest"); }); configurator.ConfigureEndpoints(context); }); }); // 添加宿主服务注册 services.AddMassTransitHostedService(); } catch (Exception ex) { throw new Exception(ex.Message, ex); }
2. 缺失ITokenFactory的依赖注册
问题:Token微服务的容器配置中未注册ITokenFactory的实现类,但TokenService的构造函数和ITokenService的工厂注册逻辑都依赖该服务,这会触发额外的解析异常,被包含在AggregateException中。
解决方案:添加ITokenFactory的服务注册,例如:
// 在注册ITokenService之前添加 services.AddScoped<ITokenFactory, TokenFactory>();
3. 服务注册顺序优化(可选)
虽然依赖注入容器在请求阶段才会解析服务,注册顺序不影响功能,但为了代码可读性和维护性,建议先注册所有基础依赖服务(如数据库上下文、ITokenRepository、ITokenFactory),再注册依赖它们的ITokenService。
调整后的注册顺序示例:
// 1. 配置TokenSettings var tokenSettings = new TokenSettings(); configuration.Bind("Token", tokenSettings); services.Configure<TokenSettings>(configuration.GetSection("Token")); // 2. 注册数据库上下文 services.AddDbContext<TokenDbContext>(options => { options.UseSqlServer(hiding this); }); // 3. 注册基础依赖服务 services.AddScoped<ITokenRepository, TokenRepository>(); services.AddScoped<ITokenFactory, TokenFactory>(); // 4. 配置MassTransit及宿主服务 services.AddMassTransit(config => { // ... 现有配置 }); services.AddMassTransitHostedService(); // 5. 注册ITokenService services.AddScoped<ITokenService>(sp => { // ... 现有代码 });
内容的提问来源于stack exchange,提问作者Rens Jespers

