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

时间:2018-10-12 01:09:01

标签: c# dependency-injection .net-core-2.1

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

它在运行时失败,并带有以下异常。我已经阅读了数十篇有关WebHostBuilder的帖子,但无法将讨论与我要使用HostBuilder进行的工作联系起来。我显然错过了一些基本知识,非常感谢您的指导。人为设计的代码示例如下。附言我相信我已经做了适当的格式化和标记工作,以安抚那些真正知道自己在做什么的人-显然我不知道!

例外

  

System.InvalidOperationException:“在尝试激活“ MyNoobProject.DoSomethingWithToDo”时无法解析类型为“ MyNoobProject.IToDo”的服务。”

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();
    }

StackOverflow.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()
    {

    }
}
}

1 个答案:

答案 0 :(得分:4)

服务提供者无法解析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>();