这里我有一个针对主题的 Azure 服务总线的后台侦听器,侦听器注册为瞬态,服务客户端注册为单例 - 像这样:
public static IServiceCollection AddASBMessageListeners(this IServiceCollection services, Assembly assembly)
{
foreach (var listener in assembly.GetTypes().Where(a => !a.IsAbstract && a.IsDefined(typeof(QueueNameAttribute))))
{
Console.WriteLine("Registering listener " + listener.Name);
services.AddTransient(typeof(IHostedService), listener);
}
return services;
}
public static IServiceCollection AddASBClient(this IServiceCollection services, IASBConfiguration configuration)
{
var clientOptions = new ServiceBusClientOptions()
{
TransportType = configuration.TransportType
};
services.AddSingleton(s => new ServiceBusClient(configuration.ConnectionString,
clientOptions));
return services;
}
后台监听器实现了
IHostedService
和IDisposable
,并且这个监听器类是用瞬态作用域注册的。
监听器类有这个方法:
public override async Task ProcessMessage(BrowserDataDeleteEvent message)
{
try
{
using var scope = _logger.BeginScope("Consuming UserAccountDeleteEvenet for {Id}", message.UserId);
var command = new DeleteUserPageVisitDataCommand(message.UserId.ToString());
await _mediator.Send(command);
_logger.LogInformation("UserAccountDeleteEvenet completed for UserId {UserId}", message.UserId);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error processing userId {UserId}", message.UserId);
throw;
}
}
在这个项目中,我使用 CQRS 模式和中介器,并且处理程序方法已注入存储库,并且存储库被注册为作用域服务,这就是异常出现的地方,当侦听器尝试执行发送方法时,它会说
来自根提供商的 MediatR.Unit,因为它需要范围服务
我已经尝试过
IServiceProvider
选项,它可以在处理程序类中使用,但不能在 Listener
类 ProcessMessage
方法中使用,是否有其他方法可以在 ProcessMessage
中使用相同的方法?
当我在侦听器类中使用选项时,如下所示:
public override async Task ProcessMessage(BrowserDataDeleteEvent message)
{
try
{
using(var scoped = _serviceProvider.CreateScope())
{
var scopedProcessingService = scoped.ServiceProvider.GetRequiredService<IMediator>();
using var scope = _logger.BeginScope("Consuming UserAccountDeleteEvenet for {Id}", message.UserId);
var command = new DeleteUserPageVisitDataCommand(message.UserId.ToString());
await scopedProcessingService.Send(command);
_logger.LogInformation("UserAccountDeleteEvenet completed for UserId {UserId}", message.UserId);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error processing userId {UserId}", message.UserId);
throw;
}
}
应用程序构建时出现以下错误:
System.AggregateException:“无法构造某些服务(验证服务描述符时出错”ServiceType:Microsoft.Extensions.Hosting.IHostedService Lifetime:Transient ImplementType:Api.Listeners.UserAccountDeletedEventListener”:无法解析类型的服务” Microsoft.Extensions.DependencyInjection.ServiceProvider”,同时尝试激活“Api.Listeners.UserAccountDeletedEventListener”。)”
提前致谢。
问题在于服务通常是有范围的,因为它们要么需要时间敏感的资源(如事务或数据库连接),要么需要在完成任务后进行清理。
单例服务永远不会结束(直到应用程序退出),并且瞬态服务没有定义的生命周期。因此,范围服务所需的东西是不可能的。
为了避免这种情况,您需要为作用域服务的调用定义生命周期,并确保正确结束生命周期(例如,如果一切顺利则提交事务)。
这样做是这样的:
using (var scope = _serviceProvider.CreateScope())
{
var service = scope.ServiceProvider.GetRequiredService<YourService>();
service.DoSomething();
scope.GetRequiredService<MyDbContext>().SaveChanges();
}
您可能希望将生命周期管理和服务解析移至特定类以隐藏服务位置。
Microsoft 还记录了有关托管服务的方法:https://learn.microsoft.com/en-us/dotnet/core/extensions/scoped-service