gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-05 05:39:37 29 4
gpt4 key购买 nike

我有以下 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)。

关于c# - 无法从根提供程序解析,因为它需要范围内的服务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72635535/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com