Multi
Multi

Reputation: 5

ASP.NET Core SignalR Adding service to hub breaks

I'm currently working on an ASP.NET Core Web Application.

I have a MQTT Server, which is connected to a service (IHostedService) and this service references a SignalR Hub. So if there is a new message comming from the MQTT Server, it is forwarded to the hub and therefore to the client.

This works fine. But now I would like to add a button to send MQTT messages back to the MQTT server.

To do so, I added a function in the hub, which es called by the button via SignalR.

So far so good but when adding the service now to the constructor of the hub it fails, when I open the web app (not during startup), with the following message:

fail: Microsoft.AspNetCore.SignalR.HubConnectionHandler[1]
      Error when dispatching 'OnConnectedAsync' on hub.
System.InvalidOperationException: Unable to resolve service for type 'websiteApp.HostedServices.UserPromptService' while attempting to activate 'websiteApp.Hubs.UserPromptHub'.
   at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)
   at lambda_method(Closure , IServiceProvider , Object[] )
   at Microsoft.AspNetCore.SignalR.Internal.DefaultHubActivator'1.Create()
   at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher'1.OnConnectedAsync(HubConnectionContext connection)
   at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher'1.OnConnectedAsync(HubConnectionContext connection)
   at Microsoft.AspNetCore.SignalR.HubConnectionHandler'1.RunHubAsync(HubConnectionContext connection) 

The service declaration looks like this:

public class UserPromptService : IHostedService, IDisposable
{

    public UserPromptService(ILogger<UserPromptService> logger, IConfiguration config, UserPromptContext userPromptContext, IHubContext<UserPromptHub> userPromptHub)
    {

    }
}

And my hub looks like this:

public class UserPromptHub : Hub<IUserPromptHub>
{
    public UserPromptHub(UserPromptService service) // everything works until I add the service here
    {
        service.ToString(); // just for testing
    }
}

And they are configured in the Startup.cs:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // ...

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
        endpoints.MapHub<Hubs.UserPromptHub>("/userPromptHub");
    });
}

As well as in the Program.cs:

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                // ...
                .ConfigureServices(services =>
                {
                    services.AddSingleton<websiteApp.DataContext.UserPromptContext>();
                    services.AddHostedService<HostedServices.UserPromptService>();
                });

Could you please help me to fix the problem?

Upvotes: 0

Views: 1266

Answers (1)

Darem
Darem

Reputation: 1759

One option to solve your problem would be to restructure your code a little bit.So instead of your UserPromptService be responsable for the MQTT connection you create a seperate class for that.

The following is only sudo code

You could create a new class

public class MQTTConnection
{
    private readonly _yourMQTTServerConnection;
    
    public MQTTConnection()
    {
        _yourMQTTServerConnection = new ServerConnection(connectionstring etc);
    }

    public Task<Message> GetMessage()
    {
       return _yourMQTTServerConnection.GetMessageAsync();
    }

    public Task SendMessage(Message message)
    {
        return _yourMQTTServerConnection.SendMessageAsync(message);
    }
}

So your Hub look something like this

public class UserPromptHub : Hub<IUserPromptHub>
{
    private readonly MQTTConnection _connection;        

    public UserPromptHub(MQTTConnection connection) 
    {
        _connection = connection
    }

    public async Task MessageYouReceiveFromTheUser(object object)
    {
        // your business logic 
       await _connection.SendMessage(message);
    }

    public Task MessageYouSendToTheClient(object object)
    {
        await Clients.All.MessageYouSendToTheClient(message);
    }
}

And your UserPromptService looks somehting like that

public class UserPromptService : IHostedService, IDisposable
{

    public UserPromptService(ILogger<UserPromptService> logger, 
                             IConfiguration config, 
                             UserPromptContext userPromptContext, 
                             IHubContext<UserPromptHub> userPromptHub,
                             MQTTConnection connection)
    {
        // map arguments to private fields
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
       while(yourAppIsUpAndRunning)
       {
          var message = await _connection.GetMessage()
          // process the message
          await _hub.MessageYouSendToTheClient(yourMessage)
       }
    }
}

I hope my approach is understandable, if not I can add more details.

Upvotes: 0

Related Questions