繁体   English   中英

无法从根提供程序解析,因为它需要范围服务

[英]Cannot resolve from root provider because it requires scoped service

我有以下 Program.cs

 public class Program
{
    public async static Task Main(string[] args)
    {

        var host = CreateHostBuilder(args).Build();

        using (var scope = host.Services.CreateScope())
        {
            var services = scope.ServiceProvider;

            try
            {
                var context = services.GetRequiredService<ProductDbContext>();

                if (context.Database.IsSqlServer())
                {
                    context.Database.Migrate();
                }
                await ProductDbContextSeed.SeedSampleDataAsync(context);
            }
            catch (Exception ex)
            {
                var logger = scope.ServiceProvider.GetRequiredService<ILogger<Program>>();

                logger.LogError(ex, "An error occurred while migrating or seeding the database.");

                throw;
            }
        }

        await host.RunAsync();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
         .UseServiceProviderFactory(new AutofacServiceProviderFactory())
            .ConfigureWebHostDefaults(webBuilder =>
                webBuilder.UseStartup<Startup>());

}

通过添加以下行:(正如我在上面的代码中已经拥有的那样):

.UseServiceProviderFactory(new AutofacServiceProviderFactory())

我的代码按我想要的方式运行。 但是,如果我删除 .UseServiceProviderFactory(new AutofacServiceProviderFactory()) 并且只需:

  public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
                webBuilder.UseStartup<Startup>());

我收到以下错误:

    Cannot resolve 'ProductAPI.IntegrationEvents.EventHandling.OrderCreatedIntegrationEventHandler' 
from root provider because it requires scoped service 
'RetailInMotion.Services.Inventory.ProductAPI.Infrastructure.Persistence.ProductDbContext'.

这是我配置 ProductDbContext 的方式:

    public static class DependencyInjection
{
    public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
    {
       
            services.AddDbContext<ProductDbContext>(options =>
                options.UseSqlServer(
                    configuration.GetConnectionString("DefaultConnection"),
                    b => b.MigrationsAssembly(typeof(ProductDbContext).Assembly.FullName)));

        services.AddDbContext<EventLogContext>(options =>
        {
            options.UseSqlServer(configuration.GetConnectionString("DefaultConnection"),
                                 sqlServerOptionsAction: sqlOptions =>
                                 {
                                     sqlOptions.MigrationsAssembly(typeof(ProductDbContext).Assembly.FullName);
                                     sqlOptions.EnableRetryOnFailure(10, TimeSpan.FromSeconds(30), null);
                                 });
        });

        services.AddAuthentication();

        services.AddAuthorization();

        return services;
    }
}

我注册我的 EventBus 如下:

private void RegisterEventBus(IServiceCollection services, IConfiguration configuration)
{
    services.AddSingleton<IEventBus, EventBusRabbitMq>(sp =>
    {
        var subscriptionClientName = configuration["SubscriptionClientName"];
        var rabbitMQPersistentConnection = sp.GetRequiredService<IRabbitMQConnectionManagementService>();
        var logger = sp.GetRequiredService<ILogger<EventBusRabbitMq>>();
        //var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>();
        var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();

        var retryCount = 5;
        if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"]))
        {
            retryCount = int.Parse(configuration["EventBusRetryCount"]);
        }
        //return new EventBusRabbitMq(eventBusSubcriptionsManager, rabbitMQPersistentConnection, iLifetimeScope, logger, retryCount, subscriptionClientName);
        return new EventBusRabbitMq(eventBusSubcriptionsManager, rabbitMQPersistentConnection, sp, logger, retryCount, subscriptionClientName);
    });

    services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
    services.AddTransient<OrderCreatedIntegrationEventHandler>();
    services.AddTransient<RemoveProductStockIntegrationEventHandler>();
}

这是我的 EventBusRabbitMq 类:

`

public class EventBusRabbitMq : IEventBus, IDisposable
{
    const string BROKER_NAME = "retailInMotion_event_bus";
    const string AUTOFAC_SCOPE_NAME = "retailInMotion_event_bus";

private readonly IEventBusSubscriptionsManager _subsManager;
private readonly IRabbitMQConnectionManagementService _rabbitMQConnectionManagementService;
private readonly IServiceProvider _serviceProvider;
//private readonly ILifetimeScope _autofac;
private readonly ILogger<EventBusRabbitMq> _logger;
private readonly int _retryCount;

private IModel _consumerChannel;
private string _queueName;

public EventBusRabbitMq(
     IEventBusSubscriptionsManager subsManager,
     IRabbitMQConnectionManagementService rabbitMQConnectionManagementService,
     IServiceProvider serviceProvider, 
     //ILifetimeScope autofac,
     ILogger<EventBusRabbitMq> logger, 
     int retryCount = 5, 
     string queueName = null)
{
    _subsManager = subsManager;
    _rabbitMQConnectionManagementService = rabbitMQConnectionManagementService;
    _serviceProvider = serviceProvider;
    //_autofac = autofac;
    _logger = logger;
    _retryCount = retryCount;
    _queueName = queueName;
    _consumerChannel = CreateConsumerChannel();
    _subsManager.OnEventRemoved += SubsManager_OnEventRemoved;
}

private void SubsManager_OnEventRemoved(object? sender, string eventName)
{
    if (!_rabbitMQConnectionManagementService.IsConnected)
    {
        _rabbitMQConnectionManagementService.TryConnect();
    }

    using (var channel = _rabbitMQConnectionManagementService.CreateModel())
    {
        channel.QueueUnbind(queue: _queueName,
            exchange: BROKER_NAME,
            routingKey: eventName);

        if (_subsManager.IsEmpty)
        {
            _queueName = string.Empty;
            _consumerChannel.Close();
        }
    }
}

private IModel? CreateConsumerChannel()
{
    if (!_rabbitMQConnectionManagementService.IsConnected)
    {
        _rabbitMQConnectionManagementService.TryConnect();
    }

    _logger.LogTrace("Creating RabbitMQ consumer channel");

    var channel = _rabbitMQConnectionManagementService.CreateModel();

    channel.ExchangeDeclare(exchange: BROKER_NAME,
                            type: "direct");

    channel.QueueDeclare(queue: _queueName,
                            durable: true,
                            exclusive: false,
                            autoDelete: false,
                            arguments: null);

    channel.CallbackException += (sender, ea) =>
    {
        _logger.LogWarning(ea.Exception, "Recreating RabbitMQ consumer channel");

        _consumerChannel.Dispose();
        _consumerChannel = CreateConsumerChannel();
        StartBasicConsume();
    };

    return channel;
}

public void Publish(IntegrationEvent @event)
{
    if (!_rabbitMQConnectionManagementService.IsConnected)
    {
        _rabbitMQConnectionManagementService.TryConnect();
    }

    var policy = RetryPolicy.Handle<BrokerUnreachableException>()
        .Or<SocketException>()
        .WaitAndRetry(_retryCount, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (ex, time) =>
        {
            _logger.LogWarning(ex, "Could not publish event: {EventId} after {Timeout}s ({ExceptionMessage})", @event.Id, $"{time.TotalSeconds:n1}", ex.Message);
        });

    var eventName = @event.GetType().Name;
    //var jsonMessage = JsonConvert.SerializeObject(@event);

    _logger.LogTrace("Creating RabbitMQ channel to publish event: {EventId} ({EventName})", @event.Id, eventName);

    using (var channel = _rabbitMQConnectionManagementService.CreateModel())
    {
        _logger.LogTrace("Declaring RabbitMQ exchange to publish event: {EventId}", @event.Id);

        channel.ExchangeDeclare(exchange: BROKER_NAME, type: "direct");

        var body = JsonSerializer.SerializeToUtf8Bytes(@event, @event.GetType(), new JsonSerializerOptions
        {
            WriteIndented = true
        });

        //var body = Encoding.UTF8.GetBytes(jsonMessage);

        policy.Execute(() =>
        {
            var properties = channel.CreateBasicProperties();
            properties.DeliveryMode = 2; // persistent

            _logger.LogTrace("Publishing event to RabbitMQ: {EventId}", @event.Id);

            channel.BasicPublish(
                exchange: BROKER_NAME,
                routingKey: eventName,
                mandatory: true,
                basicProperties: properties,
                body: body);
        });
    }
}

//public void Setup()
//{
//    throw new NotImplementedException();
//}

public void Subscribe<T, TH>()
    where T : IntegrationEvent
    where TH : IIntegrationEventHandler<T>
{
    var eventName = _subsManager.GetEventKey<T>();
    DoInternalSubscription(eventName);

    _logger.LogInformation("Subscribing to event {EventName} with {EventHandler}", eventName, typeof(TH).Name);

    _subsManager.AddSubscription<T, TH>();
    StartBasicConsume();
}
private void DoInternalSubscription(string eventName)
{
    var containsKey = _subsManager.HasSubscriptionsForEvent(eventName);
    if (!containsKey)
    {
        if (!_rabbitMQConnectionManagementService.IsConnected)
        {
            _rabbitMQConnectionManagementService.TryConnect();
        }

        _consumerChannel.QueueBind(queue: _queueName,
                            exchange: BROKER_NAME,
                            routingKey: eventName);
    }
}
public void Unsubscribe<T, TH>()
    where T : IntegrationEvent
    where TH : IIntegrationEventHandler<T>
{
    var eventName = _subsManager.GetEventKey<T>();

    _logger.LogInformation("Unsubscribing from event {EventName}", eventName);

    _subsManager.RemoveSubscription<T, TH>();
}

private void StartBasicConsume()
{
    _logger.LogTrace("Starting RabbitMQ basic consume");

    if (_consumerChannel != null)
    {
        var consumer = new AsyncEventingBasicConsumer(_consumerChannel);

        consumer.Received += Consumer_Received;

        _consumerChannel.BasicConsume(
            queue: _queueName,
            autoAck: false,
            consumer: consumer);
    }
    else
    {
        _logger.LogError("StartBasicConsume can't call on _consumerChannel == null");
    }
}

private async Task Consumer_Received(object sender, BasicDeliverEventArgs eventArgs)
{
    var eventName = eventArgs.RoutingKey;
    var message = Encoding.UTF8.GetString(eventArgs.Body.Span);

    try
    {
        if (message.ToLowerInvariant().Contains("throw-fake-exception"))
        {
            throw new InvalidOperationException($"Fake exception requested: \"{message}\"");
        }

        await ProcessEvent(eventName, message);
    }
    catch (Exception ex)
    {
        _logger.LogWarning(ex, "----- ERROR Processing message \"{Message}\"", message);
    }

    // Even on exception we take the message off the queue.
    // in a REAL WORLD app this should be handled with a Dead Letter Exchange (DLX). 
    // For more information see: https://www.rabbitmq.com/dlx.html
    _consumerChannel.BasicAck(eventArgs.DeliveryTag, multiple: false);
}

private async Task ProcessEvent(string eventName, string message)
{
    _logger.LogTrace("Processing RabbitMQ event: {EventName}", eventName);

    if (_subsManager.HasSubscriptionsForEvent(eventName))
    {
        //using (var scope = _autofac.BeginLifetimeScope(AUTOFAC_SCOPE_NAME)) // can do it this way with autofac or like directly below with built in .net core DI 
        using (var scope = _serviceProvider.CreateScope())
        {
            var subscriptions = _subsManager.GetHandlersForEvent(eventName);
            foreach (var subscription in subscriptions)
            {
                var handler = _serviceProvider.GetRequiredService(subscription.HandlerType);  //CHRIS INVESTIGATE!!
                //var handler = scope.ResolveOptional(subscription.HandlerType);
                if (handler == null) continue;
                var eventType = _subsManager.GetEventTypeByName(eventName);
                //var integrationEvent = JsonConvert.DeserializeObject(message, eventType);
                //dynamic json = Newtonsoft.Json.JsonConvert.DeserializeObject(message);
                //using dynamic eventData = JsonDocument.Parse(message);

                var integrationEvent = JsonSerializer.Deserialize(message, eventType, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true});   //JsonSerializer.Deserialize(message, eventType, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true });
                var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType);
                await (Task)concreteType.GetMethod("HandleAsync").Invoke(handler, new object[] { integrationEvent });
            }
        }
    }
    else
    {
        _logger.LogWarning("No subscription for RabbitMQ event: {EventName}", eventName);
    }
}

public void Dispose()
{
    if (_consumerChannel != null)
    {
        _consumerChannel.Dispose();
    }
    _subsManager.Clear();
}

} `

这是我收到的错误的堆栈跟踪:

at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteValidator.ValidateResolution(Type serviceType, IServiceScope scope, IServiceScope rootScope) at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope) at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType) at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType) at EventBus.EventBusRabbitMq.<ProcessEvent>d__18.MoveNext() in C:\Users\porterc\Documents\My Version of DDD\RetailInMotion-master\EventBus\EventBusRabbitMq.cs:line 260 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult() at EventBus.EventBusRabbitMq.<Consumer_Received>d__17.MoveNext() in C:\Users\porterc\Documents\My Version of DDD\RetailInMotion-master\EventBus\EventBusRabbitMq.cs:line 235

我的问题是为什么? 如果有人能指出我正确的方向或告诉我这一行实际上是做什么的,我将不胜感激。

正如异常消息所说,您的ProductAPI.IntegrationEvents.EventHandling.OrderCreatedIntegrationEventHandler未在范围内解析或未注册为范围服务。

无论您使用什么事件分派器,都应该在解析事件处理程序和处理事件之前开始一个范围。 并且OrderCreatedIntegrationEventHandler也必须注册为服务范围。

我的猜测是 Autofac 在启动时不会执行任何生命周期不匹配检查,这就是为什么当你插入 Autofac 时它不会抛出。 这并不意味着它确实有效,只是你隐藏了一个潜在的错误。 它可能会在您第一次处理事件时作为异常出现,或者可能更严重,因为它在事件处理程序的生命周期内使用相同的 ProductDbContext,这可能是单例的。

但请注意,我没有使用 Autofac,只使用了其他容器,所以我不知道这个库究竟是如何处理这些问题的。

编辑 1

这一行 (260): var handler = _serviceProvider.GetRequiredService(subscription.HandlerType); 在提供的 EventBusRabbitMq 源中需要更改为: var handler = scope.ServiceProvicer.GetRequiredService(subscription.HandlerType); .

在范围内,您应该使用范围解析您的服务(而不是从您创建范围的根服务提供者_serviceProvider )。

试试这个,我认为这可能是为了对它的构造顺序做更多的事情。

Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder => {
                webBuilder.UseStartup<Startup>();
            })
            .UseServiceProviderFactory(new AutofacServiceProviderFactory());

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM