Dependency Injection for Worker Service Issue

1

Firstly I should say I'm historically a VB.Net hobbyist programmer so please be gentle with me around C#!

I have a .NET 4.8 windows service self hosting a SignalR Hub in my app that works well (thanks to great example by Rick Strahl).

I want to migrate this service to a more platform agnostic service and the .NET Core Worker Service seems the ideal choice but in Vs2019 there isn't a VB.Net Template (only C#) so I'm venturing into new territory and learning which is good in having a real use-case to develop.

The code below is basically the strawman of the OOTB Work Service Template with SignalR grafted on; I haven't really got past the basics yet.

It compiles OK without error but when I try and debug in VS I get this error and it has me stumped despite having looked up many examples of doing this on this site and others; as far as I can see I have coded it as others have done but it fails on the CreateHostBuilder line?

System.AggregateException
  HResult=0x80131500
  Message=Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Microsoft.Extensions.Hosting.IHostedService Lifetime: Singleton ImplementationType: ModbusWorkerService.ModbusWorker': Unable to resolve service for type 'Microsoft.AspNetCore.SignalR.IHubContext`2[ModbusWorkerService.ModbusHub,ModbusWorkerService.IModbus]' while attempting to activate 'ModbusWorkerService.ModbusWorker'.)
  Source=Microsoft.Extensions.DependencyInjection
  StackTrace:
   at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(IEnumerable`1 serviceDescriptors, IServiceProviderEngine engine, ServiceProviderOptions options)
   at Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(IServiceCollection services, ServiceProviderOptions options)
   at Microsoft.Extensions.DependencyInjection.DefaultServiceProviderFactory.CreateServiceProvider(IServiceCollection containerBuilder)
   at Microsoft.Extensions.Hosting.Internal.ServiceFactoryAdapter`1.CreateServiceProvider(Object containerBuilder)
   at Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider()
   at Microsoft.Extensions.Hosting.HostBuilder.Build()
   at ModbusWorkerService.Program.Main(String[] args) in D:\Development\Comfort\Plugins\Modbus\ModbusWorkerService\Program.cs:line 12

  This exception was originally thrown at this call stack:
    [External Code]

Inner Exception 1:
InvalidOperationException: Error while validating the service descriptor 'ServiceType: Microsoft.Extensions.Hosting.IHostedService Lifetime: Singleton ImplementationType: ModbusWorkerService.ModbusWorker': Unable to resolve service for type 'Microsoft.AspNetCore.SignalR.IHubContext`2[ModbusWorkerService.ModbusHub,ModbusWorkerService.IModbus]' while attempting to activate 'ModbusWorkerService.ModbusWorker'.

Inner Exception 2:
InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.SignalR.IHubContext`2[ModbusWorkerService.ModbusHub,ModbusWorkerService.IModbus]' while attempting to activate 'ModbusWorkerService.ModbusWorker'.

It could be my c# inexperience (as DI in VB.Net is second nature to me) but can anyone offer me any guidance on what I am doing wrong.

Code so far below.

Regards

Julian


Here is Program.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace ModbusWorkerService
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args)
                .Build().Run();
        
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureServices((hostContext, services) =>
                {
                    services.AddHostedService<ModbusWorker>();
                });
    }

    public class Startup
    {
        public static void ConfigureServices(IServiceCollection services)
        {
            services.AddSignalR();
            services.AddHostedService<ModbusWorker>();

        }

        public static void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseRouting();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapHub<ModbusHub>("/hubs/Modbus");
            });
        }
    }
}

Here is my worker:

using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace ModbusWorkerService
{
    public class ModbusWorker : BackgroundService
    {
        private readonly ILogger<ModbusWorker> _logger;
        private readonly IHubContext<ModbusHub, IModbus> _ModbusHub;

        public ModbusWorker(ILogger<ModbusWorker> logger, IHubContext<ModbusHub, IModbus> ModbusHub)
        {
            _logger = logger;
            _ModbusHub = ModbusHub;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
                await _ModbusHub.Clients.All.ShowTime(DateTime.Now);
                await Task.Delay(1000, stoppingToken);
            }
        }
    }
}

and finally my SignalR hub:


using System;
using System.Threading.Tasks;
using System.Collections.Concurrent;
using Microsoft.AspNetCore.SignalR;

namespace ModbusWorkerService
{
    public class ModbusHub : Hub<IModbus>
    {
        public static ConcurrentDictionary<string, string> ConnectedUsers = new ConcurrentDictionary<string, string>();
       //public static BackgroundService HubHost;

        [HubMethodName("SendMessage")]
        public async Task SendMessage(string user)
        {
            await Clients.All.SendAsync("ReceiveGreeting", $"Good Morning {user}");
        }

        public override async Task OnConnectedAsync()
        {
            string ClientID = GetClientID();
            string HostName = GetHostName();

            if (!ConnectedUsers.ContainsKey(ClientID))
                ConnectedUsers.TryAdd(ClientID, HostName);

            // Clients.All.ShowUsersOnLine(ConnectedUsers.Count);

            await base.OnConnectedAsync();
        }

        public override async Task OnDisconnectedAsync(Exception exception)
        {
            string ClientID = GetClientID();

            if (ConnectedUsers.ContainsKey(ClientID))
                ConnectedUsers.TryRemove(ClientID, out _);

            // Clients.All.ShowUsersOnLine(ConnectedUsers.Count);

            await base.OnDisconnectedAsync(exception);
        }

        private string GetClientID()
        {
            string ClientID = "";
            //if (!(Context.QueryString['ClientID'] == null))
            //    // ClientID passed from application 
            //    ClientID = Context.QueryString['ClientID'].ToString();

            if (ClientID.Trim() == "")
                ClientID = Context.ConnectionId;

            return ClientID;
        }

        private string GetHostName()
        {
            string HostName = "";
            //if (!(Context.QueryString['HostName'] == null))
            //    // ClientID passed from application 
            //    HostName = Context.QueryString['HostName'].ToString();

            if (HostName.Trim() == "")
                HostName = "Not Known";
            return HostName;
        }

    }

    public interface IModbus
    {
        Task ShowTime(DateTime currentTime);

        Task SendAsync(string Method, string Values);
    }
}
.net
asp.net-core
signalr
asked on Stack Overflow Dec 29, 2020 by Julian Wigman

1 Answer

0

I think you forgot to use startup.

Replace following lines from program.cs

public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureServices((hostContext, services) =>
            {
                services.AddHostedService<ModbusWorker>();
            });

To

public static IHostBuilder CreateWebHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
answered on Stack Overflow Dec 29, 2020 by Hannan Hossain

User contributions licensed under CC BY-SA 3.0