2

I have a WebAPI project where the API, Service and Data layers are all in separate projects of the same solution. As part of a method in my Service project, I want to send a message to the connected clients of a hub in the API project. So far all of the examples I have found have everything in a single project and use a controller as the example sending a message via a hub.

I've tried dependency injection (Autofac) however I am unable to get a reference to the MessageHub.

enter image description here

[HubName("messages")]
public class MessageHub : Hub
{
    public void ShowNewMessage(string message)
    {
        Clients.All.showMessageOnPage(message);
    }
}

My attempt at Injecting can be seen here: Inject SignalR IHubContext into service layer with Autofac

Connie DeCinko
  • 802
  • 2
  • 15
  • 32
  • Well, you cannot reference the MessageHub class because your Service project probably does not reference WebAPI project. – Dawid Dec 07 '20 at 20:35
  • @Dawid I understand that. I cannot add the reference as that would create a circular reference. I need to be able to send a hub message from inside my service layer. – Connie DeCinko Dec 07 '20 at 20:50
  • You can put the Hub in a "shared project" that both projects then reference. – Brennan Dec 07 '20 at 21:11
  • @Brennan I haven't seen that done before. Would that shared project be a web project? The javascript client still has to be able to reach the hub. – Connie DeCinko Dec 07 '20 at 22:45

1 Answers1

1

Please review this option:

  1. Define generic hub interface in your Service (or better Domain) Layer project. Something like IMessageBroker.
  2. Inside your Presentation Layer (WebAPI) project implement this interface and use IConnectionManager for HubContext retrieving.
  3. Register the interface in an IoC Container (Autofac) in the Presentation Layer
  4. Inject the interface inside App Service

Pseudo Code:

Domain Layer:

public interface IMessageBroker
{
    void ShowNewMessage(string message)
}

Service Layer:

public class NotificationService: INotificationService
{
    private readonly IMessageBroker _messageBroker;

    public NotificationService(IMessageBroker messageBroker)
    {
        _messageBroker = messageBroker;
    }

    public void RunNotification(string message)
    {
        _messageBroker.ShowNewMessage(message);
    }
}

Presentation Layer:

[HubName("messages")]
public class MessageHub: Hub
{
    public void ShowNewMessage(string message)
    {
        Clients.All.showMessageOnPage(message);
    }
}


public class MessageBroker: IMessageBroker
{
    private readonly IConnectionManager _connectionManager;

    public MessageBroker(IConnectionManager connectionManager)
    {
        _connectionManager = connectionManager;
    }

    public void ShowNewMessage(string message)
    {
        var hub = _connectionManager.GetHubContext<MessageHub>();
        // Use Hub Context and send message
    }
}

Autofac Registration (Presentation Layer):

// Register Hubs
builder.RegisterHubs(Assembly.GetExecutingAssembly());

// Register Autofac resolver into container to be set into HubConfiguration later
builder.RegisterType<AutofacDependencyResolver>().As<IDependencyResolver>().SingleInstance();

// Register ConnectionManager as IConnectionManager so that you can get hub context via IConnectionManager injected to your service
builder.RegisterType<ConnectionManager>().As<IConnectionManager>().SingleInstance();

// Register interface
builder.RegisterType<MessageBroker>().As<IMessageBroker>();

Also similar SO topic is available here.

Alexander I.
  • 2,380
  • 3
  • 17
  • 42
  • Is there anything special about INotificationService? I just generated it based on NotificationService. – Connie DeCinko Dec 07 '20 at 22:44
  • No special about INotificationService. It is only example. Also review very similar realization on this [post](https://support.aspnetzero.com/QA/Questions/4555/broadcast-information-from-application-service-layer). – Alexander I. Dec 08 '20 at 07:07
  • Cannot resolve parameter 'ShopAP.API.Domain.Hubs.IMessageBroker messageBroker' of constructor 'Void .ctor(ShopAP.API.Data.Repositories.Interfaces.IShopAPRepository, AutoMapper.IMapper, ShopAP.API.Domain.Hubs.IMessageBroker) – Connie DeCinko Dec 08 '20 at 15:40
  • Did you register interface in Autofac? – Alexander I. Dec 08 '20 at 15:46
  • builder.RegisterHubs(Assembly.GetExecutingAssembly()); – Connie DeCinko Dec 08 '20 at 15:51
  • You need to register interface to the specific implementation. Something like: `builder.RegisterType().As();`. Review this [link](https://autofaccn.readthedocs.io/en/latest/register/registration.html). Also very similar realization described [here](https://stackoverflow.com/questions/59483789/access-signalr-from-domain-services-and-application-layer) – Alexander I. Dec 08 '20 at 18:53
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/225686/discussion-between-connie-decinko-and-alexander-i). – Connie DeCinko Dec 08 '20 at 19:35
  • @ConnieDeCinko please review updated answer. Also review very similar SO [topic](https://stackoverflow.com/questions/59483789/access-signalr-from-domain-services-and-application-layer) – Alexander I. Dec 10 '20 at 07:02
  • we are getting so much closer. I see what this does... rather than talk to the hub directly inside the service, pass the message out to the presentation layer and it passes the message to the hub. In the MessageBroker after the comment I added hub.Clients.All.showMessageOnPage(message); Right now, no errors, everything runs. I get the initial message in the Javascript client so I know it connects to the hub. I run the service method, I hit a breakpoint in MessageBroker so that all seems to work. Only think left, I am not getting the service message in the UI. – Connie DeCinko Dec 10 '20 at 15:50
  • Hmm... It seems IConnectionManager didn't worked as expected. Inside your MessageBroker try to use `GlobalHost.ConnectionManager.GetHubContext` instead of `_connectionManager.GetHubContext()`. Now It will work fine? Please let me know. – Alexander I. Dec 10 '20 at 16:31
  • Same behavior. Is there a way to debug, to see that the message is going to the hub, and that it is being "broadcast" to the UI client? I adding logging to web.config but not seing any logs. – Connie DeCinko Dec 10 '20 at 18:24
  • @Alexandar The hub is just not being used/hit when trying to send message from inside of MessageBroker. I added a HubPipelineModule and I can see before incoming and before outgoing when the initial connection is made. – Connie DeCinko Dec 11 '20 at 20:39
  • I created a clean project and shared with you. It's already having issues with the constructor even though it all looks correct. – Connie DeCinko Dec 11 '20 at 23:36
  • I do not see shared project. Could you provide a link? – Alexander I. Dec 12 '20 at 16:28
  • That's odd as I sent you an invite: https://github.com/cdecinkoKnight/SignalRDemo-help – Connie DeCinko Dec 15 '20 at 15:47
  • @Alexandar I've started yet another thread to go with my blank project that demonstrates the issue. https://stackoverflow.com/questions/65331998/signalr-2-autofac-owin-webapi-will-not-pass-message-from-server-to-hub – Connie DeCinko Dec 16 '20 at 22:25