1
votes

ASP.NET CORE 2.1 WEB API

I have a singleton class which is a tcpip client (TcpIpSerrvice) started in Startup.cs when the API does and needs to run all the time. I cannot figure out how to access IHubContext from this singleton in order to be able to send tcp data to web front end via SignalR.

I have tried to trigger this via static Action property in sigleton class, but when this is triggered in Hub, Hub is already disposed and I get (System.ObjectDisposedException: 'Cannot access a disposed object.')

in singleton class

public static Action<string> DataArrived { get; set; }

and when in hub

TcpIpService.DataArrived = new Action<string>(MessageFromPlcReceived);

I know that this can be injected to Controllers or services, but I need this to access it from elsewhere.

I'm aware that GlobalHost is not available in new SignalR and tha IHubContext is easily injectable, but this won't work (at least so far) with singleton class instantiated separetly in Startup.cs

Any ideas how to do this?

I have found out an ugly way to do it after getting throught SignalR GitHub repo. I can do this in my Startup.cs, Configure method:

TcpIpService.HubContext = app.ApplicationServices.GetRequiredService<IHubContext<VcHub>>();

which is just creating what the old GlobalHost did before. As ugly as this is, I can't find better solution ... so far. Is there a better way doing this?

1
I don't have a lot of experience with SignalR, but is your singleton thread-safe? Will it works when two or more requests are performed simultaneously?vasily.sib
the tcp client (singleton) is tested to oblivion, so I'm not that concerned about my singleton. My concern is how it can talk to SignaR ...vidriduch
some search gave me this: GlobalHost.ConnectionManager.GetHubContext<MyHub>(); Isn't this what you looking for?vasily.sib
@vasily.sib see updated question. GlobalHost worked in old SignalR, but i sno longer available in the new one.vidriduch

1 Answers

0
votes

I didn't find a proper way around this, so I have rewritten the TcpService as IHostedService and then I was able to register this in Startup.cs and inject IHubContext into the service and access the context from required class.

...
using Microsoft.Extensions.Hosting;
...

public class HostedTcpIpService : IHostedService, IDisposable
{
    #region Declaration(s)

    private readonly ILogger<HostedTcpIpService> logger;
    private readonly IHubContext<VcHub> hubContext;

    #endregion

    #region Constructor

    public HostedTcpIpService(
        ILogger<HostedTcpIpService> logger,
        IHubContext<VcHub> hubContext)
    {
        this.logger = logger;
        this.hubContext = hubContext;
    } 

    #endregion

    #region IDisposable

    public void Dispose()
    {
        // disposing
    } 

    #endregion

    #region IHostedService

    public Task StartAsync(CancellationToken cancellationToken)
    {
        // Start Tcp connection
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        // Stop Tcp connection
    }

    #endregion

    private void OnTcpMessageReceived(string tcpMessage) 
    {
        // injected hubContext ...
        // broadcast to signalR clients
        this.hubContext.Clients.All.SendAsync("broadcastMessage", tcpMessage);
    }
}

In Startup.cs

public void ConfigureServices(IServiceCollection services) 
{ 
    ...

    services.AddSingleton<Microsoft.Extensions.Hosting.IHostedService, HostedTcpIpService>();

    ...
}