2

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

    }
}
}
4
  • you need to register IToDo. Commented Oct 12, 2018 at 1:13
  • @Daniel A. White would you kindly spell it out for me in the context of my code sample? Thanks. Commented Oct 12, 2018 at 1:22
  • 2
    Change ILogger to ILogger<ToDo>. Commented Oct 12, 2018 at 9:00
  • @ Kirk Larkin Question duly edited. Commented Oct 12, 2018 at 12:33

1 Answer 1

4

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>();
Sign up to request clarification or add additional context in comments.

3 Comments

Works great in the initial example I provided. Much appreciated. However I'm feeling a bit silly because my real world project constructor is not simple and I'm stuck again. I didn't ask the right question upfront. Sorry. Question has been edited to reflect constructor taking ILogger as parameter.
@TimDude check update based on new information. This current situation is why we ask for a minimal reproducible example (emphasis on complete) so that those trying to help have a complete pcture of the actual problem.
duly noted and thank you.For completeness of answer for anyone else - upon implementing the answer I encountered an unrelated error Unable to resolve service for type 'Microsoft.Extensions.Logging.ILogger' while attempting to activate... I found an explanation here github.com/aspnet/Logging/issues/700

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.