SignalR Cannot invoke a non-delegate type - signalr

I'm trying to learn SignalR by writing a really simple application... it basically sends "Hello" periodically (like the Stock Ticker, but a lot simpler).
Here's my hub:
public class StockTickerHub : Hub
{
public void Hello()
{
var s = StockTicker.stockTicker;
Clients.All.hello();
}
}
...and here's the code that is supposed to periodically send the messages:
public class StockTicker
{
public static StockTicker stockTicker = new StockTicker();
private Thread thread;
public StockTicker()
{
var stockTickerHub = GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>();
this.thread = new Thread(() =>
{
while (true)
{
stockTickerHub.Clients.All().hello();
Thread.Sleep(1000);
}
}
);
this.thread.Start();
}
}
I'm getting a RuntimeBinderException at stockTickerHub.Clients.All().hello();. It says:
An unhandled exception of type 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' occurred in System.Core.dll
Additional information: Cannot invoke a non-delegate type
What am I doing wrong?
Client-side JavaScript is below, just in case you need to replicate this.
<script type="text/javascript">
$(function () {
var chat = $.connection.stockTickerHub;
chat.client.hello = function () {
$("#log").append("Hello");
}
$.connection.hub.start().done(function () {
chat.server.hello();
});
});
</script>

Simply change:
stockTickerHub.Clients.All().hello();
to:
stockTickerHub.Clients.All.hello();
The debugger should have already tell you this error. I tried your code after the update. It is working.
A remark on the code design:
I wouldn't start a new sending thread in the hello event, that would start one every time this method is invoked by any client. I don't think that's what you want to do. As a smoother example you could start the ticker in Startup class. (If you want a ticker per connection override OnConnected, get the client's connection id and give them separate tickers...)

Related

How to receive broadcast message from Hub class in SignalR client using C#?

I have a scenario where one of the clients is sending a request to Hub Class method AddMessage, which in turn should broadcast that message to all clients including the one who initiated it.
The problem is that I am able to call the Hub method AddMessage from the client as shown in the following code, but I couldn't find a way to handle the broadcast message on the client side which is initiated in the Hub class using the following line.
Clients.All.NotifyMessageToClients(name, message);
SignalR Hub Class
using System;
using Microsoft.AspNet.SignalR;
using System.Threading.Tasks;
public class SignalRChatHub : Hub
{
public void AddMessage(string name, string message)
{
// Following call is supposed to notify all clients with passed parameters.
// They could have a method called NotifyMessageToClients to fetch the broadcasted message
Clients.All.NotifyMessageToClients(name, message);
}
}
SignalR Client
using System;
using Microsoft.AspNet.SignalR.Client;
public partial class Default : System.Web.UI.Page
{
HubConnection hubConnection;
IHubProxy stockTickerHubProxy;
public Default()
{
hubConnection = new HubConnection("http://localhost:6898/");
stockTickerHubProxy = hubConnection.CreateHubProxy("SignalRChatHub");
}
async public void SendAddNotification(string msgFrom, string msg)
{
// Following line calls Addmessage method in SignalRChatHub class
await stockTickerHubProxy.Invoke("Addmessage", "Ajendra", "Hello StackOverflow");
}
// I might need the method NotifyMessageToClients here... to receive broadcasted message
}
I have some idea about how to achieve the same in jQuery but not in C# by creating a client as I did above. How would I achieve this?
If the above approach doesn't make sense in any way, please suggest me the right one.
You need to listen to events from the server like this:
public partial class Default : System.Web.UI.Page
{
HubConnection hubConnection;
IHubProxy stockTickerHubProxy;
public Default()
{
hubConnection = new HubConnection("http://localhost:6898/");
stockTickerHubProxy = hubConnection.CreateHubProxy("SignalRChatHub");
// listen to server events...
// n is "name" and m is "message", but you can change to "a" and "b" or anything else...
stockTickerHubProxy.On<string, string>("NotifyMessageToClients", (n, m) =>
{
Console.WriteLine("Message received from server. Name: {0} | Message: {1}", n, m);
});
}
// "async" methods should return Task instead of void....
// unless they are event handlers for UI applications...
public async Task SendAddNotification(string msgFrom, string msg)
{
// first, start the connection...
await stockTickerHubProxy.Start();
// Following line calls Addmessage method in SignalRChatHub class
await stockTickerHubProxy.Invoke("Addmessage", "Ajendra", "Hello StackOverflow");
// you don't stop the connection, otherwise you won't be able to receive calls from the server
}
}
...if you need to update UI in WPF, for example, you should implement your event like this:
stockTickerHubProxy.On<string, string>("NotifyMessageToClients", (a,b) =>
Dispatcher.InvokeAsync(() =>
{
// update UI...
textBox.Text += string.Format("Name: {0} | Message: {1}", a, b);
})
);
I suggest reading this guide for deeper details.

SignalR and Hub Persistance

I am trying out SignalR, and i don't quite understand how to call methods from my client in a way that it calls the same hub.
i have two methods in my hub:
private ctlDataManager myManager;
public void StartConnection()
{
myManager = new ctlDataManager("test");
myManager.UpdateItemEvent += myManager_UpdateItemEvent;
myManager.Connect();
}
public void StopConnection()
{
myManager.Disconnect();
}
And in my client i try to call them like this:
var notificationHub = $.connection.notificationHub;
$.connection.hub.start()
.done(function (state) {
$("#submit").click(function (e) {
e.preventDefault();
notificationHub.server.startConnection();
return false;
});
$("#stop").click(function (e) {
e.preventDefault();
notificationHub.server.stopConnection();
return false;
});
});
Now when i click on the start button it works fine it starts it and receives data too.
But when i click the stop button it throws an instance of an object error.
It appears that 'myManager' is null. It's almost as a new hub were open. Naturally i need it to be the same one as i need to close the connection.
How can i do that?
From my understanding, the server-side hub class is not persisted. Therefore, the myManager object is created with each method call from a client. My advice would be to declare myManager elsewhere in your application that you can assure 100% up-time, and have your server-side hub methods communicate with it that way.
One way for you to verify this is to debug the constructor of your hub class. You will notice that it is called for every client->server-side method call.

SignalR Broadcast from Controller not working

I'm using the latest version of SignalR (1.0.1). I am trying to broadcast from my controller to the hub and having a few issues:
My client does not seem to be getting messages.
It doesn't seem that my hub context even has an 'addMessage' defined:
My hub:
public class SignalRPriceHub : Hub
{
public void sndMessage(string msg)
{
Clients.All.addMessage(msg + "****");
}
}
Javascript client:
<script src="#Url.Content("~/Scripts/jquery.signalR-1.0.1.min.js")"></script>
<script src="~/signalr/hubs"></script>
.....
var myHub = $.connection.signalRPriceHub;
myHub.client.addMessage = function (message) {
alert(message);
};
Controller:
var context = GlobalHost.ConnectionManager.GetHubContext<SignalRPriceHub>();
context.Clients.All.addMessage("TestOutsideMessage!!!");
A few things I noticed:
1. My hub context doesn't seem to have an 'addMessage'
{"'Microsoft.AspNet.SignalR.Hubs.ClientProxy' does not contain a definition for 'addMessage'"} System.Exception {Microsoft.CSharp.RuntimeBinder.RuntimeBinderException}
If I run the following code from the same file as my client code above, I do get a message.
var myHub = $.connection.signalRPriceHub;
$.connection.hub.start(function () {
myHub.server.sndMessage("Hello World!");
});
Can anyone shed some light as to what might be happening? Thanks all!
I'll go over the faults in your initial application which shed light to why your second bullet point makes it all work.
In your javascript client you must start the connection:
$.connection.hub.start();
You must then request the message on your hub by calling the sndMessage command:
$.connection.hub.start().done(function() {
myHub.server.sndMessage("Hello world");
});
The reason why you do not see an addMessage function on your hub context is because the hub context .Clients.All is dynamic.
One last note, in order to make the sample work you also need to make sure that in your Application_Start you have RouteTable.Routes.MapHubs(); . I assume you already have this since once you add the connection start that everything works.

How can I make a server time app with SignalR

I'm leaning SignalR. How can I write a simple app so user can see server-time in real-time using Hub. Every 1 second, server will send time from server to connected clients
you can do when you using thread.
Example Hub Class:
public class ServerTime : Hub
{
public void Start()
{
Thread thread = new Thread(Write);
thread.Start();
}
public void Write()
{
while (true)
{
Clients.settime(DateTime.Now.ToString());
Thread.Sleep(1000);
}
}
}
Example Script :
<script type="text/javascript">
$(document).ready(function () {
var time = $.connection.serverTime;
$("#btnTest").click(function () {
time.start();
});
time.settime = function (t) {
$("#Time").html(t);
};
$.connection.hub.start();
});
</script>
<div id="Time"></div>
<input id="btnTest" type="button" value="Test"/>
Thread will start working when you click btnTest.
Thread sends message to page everysecond.
Create a listener and RAISE AN EVENT when a NOTIFICATION is added :) Thus you would not have to continuously check the database :)
In Global.asax in the Application_Start(object sender, EventArgs e) method create a background thread and start it. In that thread you will need to do this to get access to your hub:
IConnectionManager connectionManager = AspNetHost.DependencyResolver
.Resolve<IConnectionManager>();
dynamic clients = connectionManager.GetClients<ServerTime>();
clients.settime(DateTime.UtcNow.ToString());
NB DateTime.UtcNow is nearly always preferable since it doesn't leap around twice a year.

Is there a way to exclude a Client from a Clients.method call in SignalR?

I am evaluating SignalR (which happens to be used with Knockoutjs) to see if we can use it to notify clients of concurrency issues. Basically user "a" saves a record and users "b,c,d,e,f,g" are notified. I basically have an example working that notifies all clients. So I think I am almost there.
I came across this link and it lead me on the current path that I am on. I have also been looking at the documentation on Github.
Basically I want to exclude the a single client from the Clients.method() call. I dont see a way to loop through the clients and check the ClientId. The only other I can see to accomplish this is to maybe look at using the groups to keep track of it, but that seemed a little cumbersome, but I was having issues with that as well.
public class TicketHub : Hub
{
static int TotalTickets = 10;
public void GetTicketCount()
{
AddToGroup("ticketClients");
Clients.setTicketCount(TotalTickets);
}
public void BuyTicket()
{
if (TotalTickets > 0)
TotalTickets -= 1;
RemoveFromGroup("ticketClients");
// This will call the method ONLY on the calling client
// Caller.updateTicketCountWithNotification(TotalTickets);
// This will call the method on ALL clients in the group
Clients["ticketClients"].updateTicketCountNotify(TotalTickets);
AddToGroup("ticketClients");
Caller.updateTicketCountDontNotify(TotalTickets);
}
}
javascript code:
<script type="text/javascript">
$(document).ready(function () {
var test = $.connection.test;
$("#btnTest").click(function () {
test.testMethod();
});
test.show = function (text, guid) {
if (guid != test.guid) //notify all clients except the caller
alert(text);
};
$.connection.hub.start(function () { test.start(); });
});
</script>
Class :
public class Test : Hub
{
public void Start()
{
Caller.guid = Guid.NewGuid();
}
public void TestMethod()
{
Clients.show("test", Caller.guid);
}
}
If you want to exclude the caller from the call to the client side method you can use:
Clients.Others.clientSideMethod();
There is also Clients.AllExcept(...) that allows excluding certain people.

Resources