Connect to different Hubs on different pages - signalr

I am building a single page app which uses sammy.js, knockout.js and SignalR. The main page (index.html) loads additional html pages into a div based upon the client side route.
I have 2 SignalR hubs, one is connected to by the initial page for server side push data and this works fine. However one of the pages which are loaded when the user navigates to it should also connect to a different hub.
In the main page I am doing the following:
window.hubReady = $.connection.hub.start()
var hub1 = $.connection.hub1;
hub1.updateReceived = function () {
alert('data from server');
}
window.hubReady.done(function() {
hub1.server.start();
});
In the second page I have:
var hub2 = $.connection.hub2;
hub2.updateReceived = function () {
alert('data from server');
}
window.hubReady.done(function() {
hub2.server.start();
});
However I never receive any updates in the second page.
Any idea where I am going wrong?

In order to receive updates from a hub you must have at least 1 client side function declared for that hub when the connection is started. Judging from the libraries you are using I'm assuming you have a single page application and therefore don't instantiate your hub2 data until the connection has already started.
So an easy fix would be to just declare a hub2 client side function alongside your hub1 client side function before start is called. If you want to add more client side functions after the connection has started you'll have to use the .on method.
AKA:
hub2.on("updateReceived", function () {
alert("data from server");
});

I have created a lib called SignalR.EventAggregatorProxy.
it proxies between server side domain events and client side code. Its designed with MVVM and SPA in mind and takes care of all the hub plumbing. Check the wiki for how to set it up.
https://github.com/AndersMalmgren/SignalR.EventAggregatorProxy/wiki
once its configured all you need to subscribe to a event is
ViewModel = function() {
signalR.eventAggregator.subscribe(MyApp.Events.TestEvent, this.onTestEvent, this);
};
MyApp.Events.TestEvent corresponds to a server side .NET event. You can also constraint which event should go to which usera

Related

SignalR for 2 web pages

I am using .net project and create one web page to use with SignalR. This code below working with one web page. Client side:
$.connection.hub.start().done(function () { });
var chat = $.connection.chatHub;
chat.client.broadcastMessage = function (name, message) { };
Server side:
var context =
Microsoft.AspNet.SignalR.GlobalHost.ConnectionManager.GetHubContext<SignalRChat.ChatHub>();
context.Clients.All.broadcastMessage(name, message);
Then I create second web page and copy client and server code from page 1.
Problem is that when I call
context.Clients.All.broadcastMessage(name, message);
from second page. it fired function
chat.client.broadcastMessage
on first page. How can I fixed that to fired client side for page 2?
Thanks
Do you want it to fire on both pages -
I suspect you did not create a connection on the page 2. Once you have a valid connection on page 2 it would fire on both page 1 and 2 if you had both open. Or only on the page you have open.
If you want it to fire on each page but not both pages you have some changes to make. You have several options you can go with here with no additional context:
Really simple - rename chat.client.broadcastMessage on page 2 to something else and call it from the server with a new method.
Look into Groups - updating context.Clients.Group(groupname).broadcastMessage(name, message) you can send messages to users on page1 (assuming you have a group for page 1 and page 2) and then only send messages to users on page 2 when needed.

Signalr server messages to client cancelled (specific clients only)

I use signalR to display currently connected users. Some clients are failing to see the connected clients because the server to client connection is being cancelled by the client as shown below:
Below is my js code:
var progressNotifier = $.connection.activeConnectionsHub;
$.connection.hub.start().done(function () {
progressNotifier.server.showActiveConnections();
// code here
});
// client-side sendMessage function that will be called from the server-side
progressNotifier.client.addConnections = function (param) {
// code here never gets called
}
This only happens on some client browsers.
I tried disabling antivirus, firewall, malware etc. But it is still not working.
Does anyone have an idea what is causing this issue?

Call frontend methods from external meteor application

I am making a dockerized services-based application. Some of the services will be written in meteor, some won't.
One of the services is a registration service, where users can register for the platform.
When doing microservices, normally I do the following:
var MyService = DDP.connect(service_url);
var MyOtherService = DDP.connect(other_service_url);
var RegistrationService = DDP.connect(registration_service_url);
What I want to do is use the loginWithFacebook method. The issue is that using Meteor.loginWithFacebook on the frontend will invoke its backend methods on the main frontend server.
However, I want to invoke its backend methods on the RegistrationService server (which has the relevant packages). The reason is because I am using the Accounts.onCreateUser hook to do extra stuff, and also because I want to keep the registration service separate from the frontend.
Just for clarity, even though it is not correct, imagine I have this:
'click #facebook-login': function() {
Meteor.loginWithFacebook(data, callback)
}
However, I want the loginWithFacebook method to use the server-side methods from RegistrationService when calling the client-side method .loginWithFacebook, so I actually want to do something to the effect of the following:
'click #facebook-login': function() {
RegistrationService.loginWithFacebook(data, callback)
}
Any help on this will be greatly appreciated. Thank you!
I believe you are looking for DDP.connect. Basically underneath meteor all calls to the server from the client and all communication from the server to the client use Distributed Data Protocol. (https://www.meteor.com/ddp) As the documentation points out by default a client opens a DDP connection to the server it is loaded from. However, in your case, you'd want to use DDP.connect to connect to other servers for various different tasks, such as a registration services server for RegistrationService. (http://docs.meteor.com/#/full/ddp_connect) As a simplified example you'll be looking to do something like this:
if (Meteor.isClient) {
var registrationServices = DDP.connect("http://your.registrationservices.com:3000");
Template.registerSomething.events({
'click #facebook-login': function(){
registrationServices.call('loginWithFacebook', data, function(error, results){ ... }); // registration services points to a different service from your default.
}
});
}
Don't forget that you can also have various DDP.connect's to your various microservices. These are akin to web service connections in other applications.
You can maybe achieve connection through your other service by specifying the service's remote connection to Accounts and Meteor.users:
var RegistrationService = DDP.connect(registration_service_url);
Accounts.connection = RegistrationService;
Meteor.users = new Meteor.Collection('users',{connection: RegistrationService});
Then would call Meteor.loginWithFacebook and it should use the other app's methods for logging in.

WCF Service with SignalR

I have a web application which has few charts on dashboard. The data for charts is fetched on document.ready function at client side invoking a WCF service method.
What i want is now to use SignalR in my application. I am really new to SignalR. How can i call WCF methods from SignalR Hub or what you can say is that instead of pulling data from server i want the WCF service to push data to client every one minute.
Is there a way of communication between signalR and WCF service.
Also another approach can be to force client to ask for data from WCF Service every minute.
Any help will be really appreciated.
I have done following as of yet.
Client Side Function on my Dashboard page
<script src="Scripts/jquery.signalR-2.0.3.min.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="/signalr/hubs"></script>
<a id="refresh">Refresh</a>
$(function() {
var dashboardHubProxy = $.connection.dashboardHub;
$.connection.hub.start().done(function() {
// dashboardHubProxy.server.refreshClient(parameters);
$("#refresh").click(function() {
dashboardHubProxy.server.refreshClient(parameters);
});
});
dashboardHubProxy.client.refreshChart = function (chartData) {
debugger;
DrawChart(chartData, 'Hourly Call Count For Last ' + Duration + ' Days', '#chartHourly', 'StackedAreaChart');
};
});
and my Dashboard Hub class is as follows
public class DashboardHub : Hub
{
private readonly ReportService ReportService = new ReportService();
public void RefreshClient(string parameters)
{
var chartData = ReportService.GenerateHourlyCallsTrendGraphicalReport(parameters);
Clients.All.refreshChart(chartData);
}
}
My SignalR startup class is as follows
[assembly: OwinStartup(typeof(CallsPortalWeb.Startup), "Configuration")]
namespace CallsPortalWeb
{
public static class Startup
{
public static void Configuration(IAppBuilder app)
{
ConfigureSignalR(app);
}
public static void ConfigureSignalR(IAppBuilder app)
{
app.MapSignalR();
}
}
}
When i click on refresh button and a debugger on RefreshClient method on hub the debugger doesn't get to the method which means i am unable to call server side method of SignalR.
Is there anything needs to be done in web.config?
I agree with AD.Net's comment. To elaborate slightly more though, the SignalR hubs can be hosted directly in your web project kinda the same way controllers are used. There is also a package out there so you can host the SignalR library on its own so it can act as a service all on its own. Either way you will need to hit the SignalR hub first as that is how it communicates then you would call your WCF service methods from within the hubs.
Brief explanation
Your HUB will have methods used by both your USER Client and your WCF Client. You may use something like UserConnected() for the user to call in and setup your logging of the connection. Then the WCF service may call your HUB with an UpdateUserStats(Guid connnectionId, UserStats stats) which would in turn call the USER client directly and provide the stats passed in like so Clients.Client(connectionId).updateStats(stats) which in turn would have a method on the USERS client named updateStats() that would handle the received information.
Initial page landing
What AD.Net provided is basic code that will be called when the user lands on the page. At this point you would want to log the ConnectionId related to that user so you can directly contact them back.
First contact with your hub touching WCF
From your Hub, you could call your WCF service as you normally would inside any normal C# code to fetch your data or perform action and return it to your user.
Method of updating the user periodically
SignalR removes the need for your client code to have to continually poll the server for updates. It is meant to allow you to push data out to the client with out them asking for it directly. This is where the persistence of the connections come into play.
You will probably want to create a wrapper to easily send messages to the hub from your application, since you are using WCF I would assume you have your business logic behind this layer so you will want the WCF service reaching out to your Hub whenever action X happens. You can do that by utilizing the Client side C# code as in this case your client is actually the user and the WCF service. With a chat application the other user is basically doing what you want your WCF service to do, which is send a message to the other client.
Usage example
You are running an online store. The dashboard displays how many orders there have been for the day. So you would wire up a call to the hub to send a message out to update the products ordered when a user places a new order. You can do this by sending it to the admin group you have configured and any admins on the dashboard would get the message. Though if these stats are very user specific, you will more then likely instead reach into the database, find the ConnectionId that the user has connected with and send the update message directly to that connectionid.
WCF Client Code Example
Just incase you want some code, this is directly from MS site on connecting with a .net client. You would use this in your WCF service, or wherever in your code you plan on connecting and then sending an update to your user.
var hubConnection = new HubConnection("http://www.contoso.com/");
IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("StockTickerHub");
stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => Console.WriteLine("Stock update for {0} new price {1}", stock.Symbol, stock.Price));
await hubConnection.Start();
Here is a link directly to the .Net Client section: http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-net-client
I am sure you have seen this link but it really holds all the good information you need to get started. http://www.asp.net/signalr
Here is a more direct link that goes into usages with code for you. http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-server
ADDED: Here is a blog specific to Dashboards with SignalR and their polling.
http://solomon-t.blogspot.com/2012/12/signalr-and-interval-polling-for.html
ADDED: Here is a page on managing users signalR connections.
http://www.asp.net/signalr/overview/signalr-20/hubs-api/mapping-users-to-connections
Update for your code update
The .Net Client library (in NuGet) gives your .net code access to the hub. Since you are a client you will need to connect to the hub just like the User who is also a client. Your hub would act as the server for this. So with the .Net Client I am assuming you would setup a windows service that would internally poll, or something event based that would call the .Net Client code portion of it which would reach out to your hub. Your hub would take the information provided, more than likely a ConnectionId or GroupId and broad cast the User (which is perhaps on a website so it would be the JS client) a method that would update the front end for the user client. Basically what I mention under "Brief Explanation".
Now, to directly respond to the code you posted. That is Javascript, I would expect a connect like you have done. Updating the chart on initial connection is fine as well. If this is all the code signalR wise though you are missing a client side method to handle the refresh. Technically, instead of calling Clients.Caller.RefreshChart() you could just return that data and use it, which is what your javascript is doing right now. You are returning void but it is expecting a your date.
Now, I would actually say correct your javascript instead of correcting the hub code. Why? Because having a method in JS on your client that is called "refreshChart()" can be reused for when you are having your server reach out and update the client.
So I would recommend, dropping anything that is related to updating the dashboard in your JS done statement. If you want to do a notification or something to the user that is fine but dont update the grid.
Now create a JS client function called "refreshChart", note the lower case R, you can call it with a big R in c# but the js library will lowercase it so when you make the function have it will receive your dashboard information.
Now, on the server polling, or executing on some action, your WCF would call a method on the hub that would be say "UpdateDashboar(connectionId,dashInfo)" and that method would then inside of it call the "refreshChart" just like you are doing in your RefreshClient method, accept instead of doing Clients.Caller you would use Clients.Client(connectionId).refreshChart(chartInfo).
Directly the reason your code is not working is because you need to turn that Void into the type you expect to be returned. If the rest is coded right you will have it update once. You will need to implement the other logic I mentioned if you want it constantly updating. Which is again why I asked about how you are persisting your connections. I added a link to help you with that if you are not sure what I am talking about.
You should use the SignalR Hub to push data to the client. Your hub can consume a WCF service (the same way your client can) to get the data.
from client:
hub.VisitingDashBoard();
on the hub in the VisitingDashBoard method:
var data = wcfClient.GetDashboardData()//may be pass the user id from the context
Clients.Caller.UpdateDashboard(data)
Of course your client will have a handler for UpdateDashboard call

what is the best design to use connections in signalr

I have some doubts regarding my software design with signalr,
usually I start with this code:
var connection = $.hubConnection();
var notifier = connection.createHubProxy('notifier');
connection.start()
.done(function () {
//alert('connection was succesful');
// your event handlers
});
UPDATED QUESTION
If you have a common click event handler that will only use signalR to broadcast a message, and this event handler is on a separate javascript file (out of the context of the current connection):
should I open the connection in the external script? or
should I send a reference of the proxy (already connected) to the external script?
When your page doesn't need a connection you should not open one.

Resources