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

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

这里我有一个针对主题的 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”。)”

提前致谢。

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

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

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

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

这样做是这样的:

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.