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);
}
}
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>(); });
User contributions licensed under CC BY-SA 3.0