如何在瞬态或单例服务中运行作用域服务

问题描述 投票:0回答:1

这里我有一个用于主题的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'.)'

提前致谢。

c# .net asp.net-core .net-core
1个回答
0
投票

问题在于服务通常是有范围的,因为它们要么需要时间敏感的资源(如事务或数据库连接),要么需要在完成任务后进行清理。

单例服务永远不会结束(直到应用程序退出),并且瞬态服务没有定义的生命周期。因此,范围服务所需的东西是不可能的。

为了避免这种情况,您需要为作用域服务的调用定义生命周期,并确保正确结束生命周期(例如,如果一切顺利则提交事务)。

这样做是这样的:

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

© www.soinside.com 2019 - 2024. All rights reserved.