2
votes

I have a SignalR client in a Windows Service that successfully calls a Server method in an MVC app. First the Server Code:

 public class AlphaHub : Hub
    {
        public void Hello(string message)
        {
            // We got the string from the Windows Service 
            // using SignalR. Now need to send to the clients
            Clients.All.addNewMessageToPage(message);

            // Send message to Windows Service

        }

and

public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            ConfigureAuth(app);
            app.MapSignalR("/signalr", new HubConfiguration());
        }
    }

The Windows Service client is:

protected override async void OnStart(string[] args)
        {
            eventLog1.WriteEntry("In OnStart");
            try
            {
                var hubConnection = new HubConnection("http://localhost.com/signalr", useDefaultUrl: false);
                IHubProxy alphaProxy = hubConnection.CreateHubProxy("AlphaHub");

                await hubConnection.Start();

                await alphaProxy.Invoke("Hello", "Message from Service");
            }
            catch (Exception ex)
            {
                eventLog1.WriteEntry(ex.Message);
            }
        }

It sends a message to the MVC Server. Now I want to call the other way from server to client. The Client Programming Guide has the following code examples which will NOT work as this is not a desktop.

WinRT Client code for method called from server without parameters (see WPF and Silverlight examples later in this topic)
var hubConnection = new HubConnection("http://www.contoso.com/");
IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("StockTickerHub");
stockTickerHub.On("notify", () =>
    // Context is a reference to SynchronizationContext.Current
    Context.Post(delegate
    {
        textBox.Text += "Notified!\n";
    }, null)
);
await hubConnection.Start();

How can I call a method on the client?

2
So you want to call a Hub method inside your Windows service from another Hub in your MVC app, correct? Would it be acceptable to call your Windows service Hub method from Javascript in your MVC app?rwisch45
No, that's incorrect. I want to call a method on my Windows Service client using the same Hub defined in the MVC app.user2471435
But if I need another Hub o make it work and you can show me the complete code, I'll go for it.user2471435
I use a Windows service that self hosts SignalR, and I use it to communicate with an MVC app (MVC to service and vice versa), but it's through the Javascript in the MVC app - not a Hub. If that would help I could post that coderwisch45
yes please (my SignalR is hosted in the MVC app but that shouldn't matter)user2471435

2 Answers

4
votes

The .NET client side code seems fine. You can simply get rid of Context.Post since your client is running inside of a Windows Service and doesn't need a SyncContext:

protected override async void OnStart(string[] args)
{
    eventLog1.WriteEntry("In OnStart");
    try
    {
        var hubConnection = new HubConnection("http://localhost.com/signalr", useDefaultUrl: false);
        IHubProxy alphaProxy = hubConnection.CreateHubProxy("AlphaHub");

        stockTickerHub.On("Notify", () => eventLog1.WriteEntry("Notified!"));

        await hubConnection.Start();

        await alphaProxy.Invoke("Hello", "Message from Service");
     }
     catch (Exception ex)
     {
            eventLog1.WriteEntry(ex.Message);
     }
}

You can invoke the "Notify" callback from inside your AlphaHub on the server like so:

public class AlphaHub : Hub
{
    public void Hello(string message)
    {
        // We got the string from the Windows Service 
        // using SignalR. Now need to send to the clients
        Clients.All.addNewMessageToPage(message);

        // Send message to the Windows Service
        Clients.All.Notify();
    }

Any client will be able to listen to these notifications since we are using Clients.All. If you want to avoid this, you need some way to authenticate your Windows Service and get its ConnectionId. Once you have that, you can send to the Windows Service specifically like so:

Clients.Client(serviceConnectionId).Notify();
0
votes

Hope this helps.

Windows Service with self hosted SignalR

public partial class MyWindowsService : ServiceBase
    {
        IDisposable SignalR { get; set; }

        public class SignalRStartup
        {
            public static IAppBuilder App = null;

            public void Configuration(IAppBuilder app)
            {
                app.Map("/signalr", map =>
                {
                    map.UseCors(CorsOptions.AllowAll);
                    var hubConfiguration = new HubConfiguration()
                    {
                        // EnableDetailedErrors = true
                    };

                    map.RunSignalR(hubConfiguration);
                });

            }


        }       

        public MyWindowsService()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args) { Start(); }

        protected override void OnStop() { Stop(); }

        public void Start()
        {
            SignalR = WebApp.Start<SignalRStartup>("http://localhost:8085/signalr");
            CallToMvcJavascript();
        }

        public new void Stop()
        {
            SignalR.Dispose();
        }

    private void CallToMvcJavascript(){
      GlobalHost.ConnectionManager.GetHubContext<MyHub>().Clients.All.addNotice(// object/data to send//);
     }
}

The Hub in the Windows Service

public class MyHub : Hub
{
    public void Send()
    {
        Clients.All.confirmSend("The service received the client message");
    }
}

The Javascript

  $.connection.hub.logging = true;
   $.connection.hub.url = "http://localhost:8085/signalr";
   var notices = $.connection.myHub;

notices.client.addNotice = function(notice) {
    console.log(notice);
};

notices.client.confirmSend = function(msg) {
    alert(msg);
};

$.connection.hub.start().done(function() {
    $('#myTestBtn').on('click', function() {
        notices.server.send();
    });
});