这里我有一个用于主题的Azure服务总线的后台监听器,监听器注册为Transient,服务客户端注册为Singleton,如下所示,
public static IServiceCollection AddASBMessageListners(this IServiceCollection services, Assembly assembly)
{
foreach (var listener in assembly.GetTypes().Where(a => !a.IsAbstract && a.IsDefined(typeof(QueueNameAttribute))))
{
Console.WriteLine("Registering listner " +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
,并且这个监听器类是在Transient范围内注册的。
并且监听器类有这个方法,
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 from root provider because it requires scoped service <Repository>
我已经尝试过
IServiceProvider
选项,它在处理程序类中工作,但不在监听器类 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: 'Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Microsoft.Extensions.Hosting.IHostedService Lifetime: Transient ImplementationType: Api.Listeners.UserAccountDeletedEventListener': Unable to resolve service for type 'Microsoft.Extensions.DependencyInjection.ServiceProvider' while attempting to activate '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