golfalot
golfalot

Reputation: 1284

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

My intent is simply to call one micro Service from another within a Console app using the Generic Host.

It fails at runtime with the below Exception. I've read dozens of posts relating to WebHostBuilder but can't relate the discussions to what I'm trying to do with HostBuilder. I'm obviously missing something fundamental and would really appreciate a steer. A contrived code sample follows. P.S. I trust I've done a suitable job of formatting and tagging to appease those who really know what they're doing - evidently I don't!

Exception

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

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

    }
}
}

Upvotes: 2

Views: 4151

Answers (1)

Nkosi
Nkosi

Reputation: 246998

The service provider is unable to resolve the IToDo interface because it was not registered with the service collection.

So you have already registered the ToDo implementation as a singleton hosted service.

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

I would suggest restructuring it bit if you want to use the interface in the constructor of the other service.

Since the service has a simple constructor you can initialize it in the composition root and apply the necessary registration

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

That way the interface injected into the constructor of DoSomethingWithToDo will use the singleton defined at startup

If ToDo also has dependencies via constructor injection, it can follow a similar pattern, but now we would need to use the delegate factory to defer initialization so that dependencies can be resolved and injected.

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

Upvotes: 4

Related Questions