无法解析 c# .NET Core 的 Webapp 程序 .cs 中的实例

Unable to resolve the instance in Webapp program.cs in c# .NET Core

提问人:Vikneshwaran Seetharaman 提问时间:9/23/2023 最后编辑:Vikneshwaran Seetharaman 更新时间:9/23/2023 访问量:46

问:

我有 4 个 c# 项目

  • WebApp(ASP.NET 核心 MVC Web 应用程序)
  • EmailNotificationService(类库)
  • PushNotificationService(类库)
  • EventBroker(类库)

在 Web 应用程序中,我运行了一个后台服务,它持续侦听队列(作为单例注入)。队列由 Web 应用更新,后台服务从队列中读取并处理每个项目,然后将其上传到数据库。后台服务还包含一个实例(注入到单例中),它使用该实例发布上载事件EventManager

public class UploadBackgroundService : BackgroundService
{
    private readonly UploadQueue _workerQueue;
    private readonly IServiceProvider _serviceProvider;
    private readonly ILogger<UploadQueue> _logger;
    private readonly EventManager _eventManager;

    public UploadBackgroundService (UploadQueue workerQueue, IServiceProvider serviceProvider, ILogger<UploadQueue > logger, EventManager eventManager)
    {
        _workerQueue = workerQueue ?? throw new ArgumentNullException(nameof(workerQueue));
        _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
        _eventManager = eventManager ?? throw new ArgumentNullException(nameof(eventManager));
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                using IServiceScope scope = _serviceProvider.CreateScope();
                using DbContext dbContext = scope.ServiceProvider.GetService<DbContext >() ?? throw new Exception("Database context is null!");

                await foreach(Func<DbContext , Task<Models.UploadResult>> workItem in this._workerQueue.ReadAsyncStream())
                {
                    UploadResult uploadResult = await workItem(dbContext);
                    _eventManager.NotifyObservers(new UploadResult(Enums.UploadStatus.Error));
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }
    }
}

该项目包含一个类,该类创建上传事件并通知其所有订阅者。EventBrokerEventManager

public class EventManager
{
    public event EventHandler<UploadEventArgs>? UploadEvent;

    public  virtual void OnUpload(UploadEventArgs uploadEventArgs)
    {
        UploadEvent?.Invoke(this, uploadEventArgs);
    }

    public void NotifyObservers(object uploadResult)
    {
        UploadEventArgs uploadEventArgs = new UploadEventArgs();
        uploadEventArgs.Data = JsonConvert.SerializeObject(uploadResult);
        OnUpload(uploadEventArgs);
    }
    
}

包含一个电子邮件使用者类,该类订阅 EventManager 中的上传事件,并在收到通知时向用户发送电子邮件。EmailNotificationsService

public class EmailConsumer : IConsumer
{
    private readonly IEmailSender _emailSender;

    public EmailConsumer(IEmailSender emailSender)
    {
        _emailSender = emailSender ?? throw new ArgumentNullException(nameof(emailSender));
    }

    public void Subscribe(EventManager eventBroker)
    {
        eventBroker.UploadEvent += SendEmailNotifications!;
    }

    public void SendEmailNotifications(object sender, UploadEventArgs eventArgs)
    {
        EmailUploadResult uploadResult = JsonConvert.DeserializeObject<EmailUploadResult>(eventArgs.Data) ?? throw new InvalidDataException("Corrupt data");
        _emailSender.SendEmailAsync(null, new List<Address> { new Address("testcapsdev@outlook.com") }, null, null, null, uploadResult);
    }
}

包含 signalRNotification 使用者类,该类订阅 EventManager 中的上传事件,并在收到通知时向 UI 发送推送通知。PushNotificationsService

public class SignalRNotificationConsumer: IConsumer
{
    private readonly IHubContext<NotificationHub, INotificationHub> _hubContext;

    public SignalRNotificationConsumer(IHubContext<NotificationHub, INotificationHub> hubContext)
    {
        _hubContext = hubContext;
    }

    public void Subscribe(EventManager eventBroker)
    {
        eventBroker.UploadEvent += SendSignalRNotifications!;
    }

    private void SendSignalRNotifications(object sender, UploadEventArgs uploadEventArgs)
    {
        try
        {
            SignalRUploadResult uploadResult = JsonConvert.DeserializeObject<SignalRUploadResult >(uploadEventArgs.Data) ?? throw new InvalidDataException("Corrupt data");
            _hubContext.Clients.All.UploadNotifications(uploadResult);
        }
        catch(Exception ex)
        {
            Console.WriteLine(ex);
        }
    }
}

现在我正在订阅网络应用程序内的事件:program.cs

var services = builder.Services;

services.AddSingleton<UploadQueue, UploadQueue>();
services.AddSingleton<EventManager, EventManager>();
services.AddScoped<SignalRNotificationConsumer>().AddScoped<IConsumer, SignalRNotificationConsumer>(s => s.GetService<SignalRNotificationConsumer>()!);
services.AddScoped<EmailConsumer>().AddScoped<IConsumer, EmailConsumer>(s => s.GetService<EmailConsumer>()!);
services.AddScoped<IEmailSender, EmailSender>();

var app = builder.Build();

EventManager eventBroker = app.ServiceProvider.GetService<EventManager>()!;
IConsumer consumer = (IConsumer)app.ServiceProvider.GetService(typeof(EmailConsumer))!;
consumer.Subscribe(eventBroker);
IConsumer consumer =  (IConsumer)app.ServiceProvider.GetService(typeof(SignalRNotificationConsumer))!;
consumer.Subscribe(eventBroker);

现在我收到一个错误

无法解析来自根提供程序的作用域服务

我不想在后台服务中订阅,该服务工作正常。因为后台服务不必知道有关订阅者的任何信息。

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    IConsumer signalRConsumer = (IConsumer)_serviceProvider.CreateScope().ServiceProvider.GetService(typeof(SignalRNotificationConsumer))! ?? throw new ArgumentNullException("SignalRConsumer");
    IConsumer emailConsumer = (IConsumer)_serviceProvider.CreateScope().ServiceProvider.GetService(typeof(EmailConsumer))! ?? throw new ArgumentNullException("SignalRConsumer");

    signalRConsumer.Subscribe(_eventManager);
    emailConsumer.Subscribe(_eventManager);

    while (!stoppingToken.IsCancellationRequested)
    {
            try
            {
                using IServiceScope scope = _serviceProvider.CreateScope();
                using DbContext dbContext = scope.ServiceProvider.GetService<DbContext >() ?? throw new Exception("Database context is null!");

                await foreach(Func<DbContext , Task<Models.UploadResult>> workItem in this._workerQueue.ReadAsyncStream())
                {
                    UploadResult uploadResult = await workItem(dbContext);
                    _eventManager.NotifyObservers(new UploadResult(Enums.UploadStatus.Error));
                }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
    }
}

上面的代码工作正常。

所以有人可以指出正确的方向。我的想法合适吗?以及如何在不让后台服务初始化订阅者的情况下完成这项工作。

C# ASP.NET Core 事件 依赖项注入

评论

1赞 jdweng 9/23/2023
在启动后台服务之前,您需要将订阅者传递给后台服务并初始化订阅者。如果为订阅者放置一个类,则可以将整个类传递给后台服务。类对象由 Ref 传递,因此主类和后台类将看到更改。

答: 暂无答案