ASP.NET 核心通用主机 (HostBuilder) 尝试激活时无法解析类型的服务...

ASP.NET Core Generic Host (HostBuilder) Unable to resolve service for type ... while attempting to activate

我的意图只是使用通用主机在控制台应用程序中从另一个微服务调用一个微服务。

它在运行时失败并出现以下异常。我已经阅读了许多与 WebHostBuilder 相关的帖子,但无法将讨论与我尝试使用 HostBuilder 进行的操作联系起来。我显然缺少一些基本的东西,非常感谢指导。下面是一个人为设计的代码示例。 P.S。我相信我已经做了适当的格式化和标记工作,以安抚那些真正知道自己在做什么的人 - 显然我不知道!

异常

System.InvalidOperationException: 'Unable to resolve service for type 'MyNoobProject.IToDo' while attempting to activate 'MyNoobProject.DoSomethingWithToDo'.'

Program.cs

using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

public static async Task Main(string[] args)
    {
        var builder = new HostBuilder()
            .ConfigureServices((hostContext, services) =>
            {
                services.AddSingleton<IHostedService, ToDo>();
                services.AddSingleton<IHostedService, DoSomethingWithToDo>();

            })                
            .ConfigureLogging((hostingContext, logging) => {
               logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
                logging.AddConsole();
            });;

        await builder.RunConsoleAsync();
    }

Whosebug.cs

using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

namespace MyNoobProject
{
interface IToDo
{
    Task Add(int i);
    void Dispose();
}

class ToDo : IHostedService, IDisposable, IToDo
{
    private BlockingCollection<int> _blockingCollection;

    private readonly ILogger _logger;

    public ToDo(ILogger<ToDo>logger)
    {
        _logger = logger;
    }

    public Task Add(int i)
    {
        _blockingCollection.Add(i);
        return Task.CompletedTask;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _blockingCollection = new BlockingCollection<int>();
        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _blockingCollection.CompleteAdding();
        return Task.CompletedTask;
    }

    public void Dispose()
    {
        _blockingCollection.Dispose();
    }
}

interface IDoSomethingWithToDo
{

}

class DoSomethingWithToDo : IHostedService, IDisposable, IDoSomethingWithToDo
{
    private IToDo _todo;

    public DoSomethingWithToDo(IToDo todo)
    {
        _todo = todo;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _todo.Add(1);
        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }

    public void Dispose()
    {

    }
}
}

服务提供者无法解析 IToDo 接口,因为它没有在服务集合中注册。

因此您已经将 ToDo 实施注册为单例托管服务。

services.AddSingleton<IHostedService, ToDo>();
services.AddSingleton<IHostedService, DoSomethingWithToDo>();

如果您想在其他服务的构造函数中使用该接口,我建议您对其进行一些重构。

由于该服务有一个简单的构造函数,您可以在组合根中对其进行初始化并应用必要的注册

var todo = new ToDo();
services.AddSingleton<IToDo>(todo);
services.AddSingleton<ToDo>(todo);
services.AddSingleton<IHostedService>(todo);
services.AddSingleton<IHostedService, DoSomethingWithToDo>();

这样,注入到 DoSomethingWithToDo 的构造函数中的接口将使用启动时定义的单例

如果ToDo也有通过构造函数注入的依赖项,它可以遵循类似的模式,但现在我们需要使用委托工厂来延迟初始化,以便可以解析和注入依赖项。

//adding implementation first
services.AddSingleton<ToDo>();
//adding options for interfaces
services.AddSingleton<IToDo>(_ => _.GetService<ToDo>());
services.AddSingleton<IHostedService>(_ => _.GetService<ToDo>());
//...
services.AddSingleton<IHostedService, DoSomethingWithToDo>();