Calling SignalR Hub method from code behind - asp.net

I had a web Application with master page and I need to show a alert to all connected users of my application. I had used the Owin startup class and mapped signal R
Then created a Hub like below
namespace ArtWebApp
{
[HubName("artHub")]
public class ArtWebHub : Hub
{
public void Hello()
{
Clients.All.hello();
}
public void SayMessage()
{
this.Clients.All.showmessage();
}
}
}
Then in the masterpage I added the Javascript as below
<script src="Scripts/jquery-1.6.4.js"></script>
<script src="Scripts/jquery.signalR-2.2.1.js"></script>
<script type="text/javascript">
$(function () {
debugger;
var connection = $.hubConnection("")
var hub = connection.createHubProxy('artHub');
hub.on('showmessage', function () {
alert('Hi');
});
connection.start().done();
//connection.start(function () {
// hub.invoke('SayMessage');
//});
});
</script>
This is working perfectly when the Hub method is invoked from the same page but when I tried to call the method from button click of a page its not working
protected void Button1_Click(object sender, EventArgs e)
{ var hubContext = GlobalHost.ConnectionManager.GetHubContext<ArtWebApp.ArtWebHub>();
hubContext.Clients.All.SayMessage();
}
Can somebody suggest me the issue

What i can see from your code is a mistake on the client side function you are calling.
Clients.All typically lets you invoke a function you have defined at the client side by calling Clients.All.functionName()
In the Button1_Click event please change
hubContext.Clients.All.SayMessage();
To
hubContext.Clients.All.showMessage();
This is because you are using the dynamic collection Clients
You are trying to invoke a client side function (which doesn't exist).
The method SayMessage you are trying to call is a member of the ArtWebHub class and cannot be invoked by calling hubContext.Clients.All.
You can invoke SayMessage from the client using hub.invoke('SayMessage') but to invoke the showmessage function defined in the client you'll have to invoke it differently from the server because SayMessage is not available to the hubContext

Related

SignalR Cannot invoke a non-delegate type

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...)

SignalR hub hangs on IIS

We use SignalR library in our ASP.NET web application. The code looks as following:
Server:
[HubName("ticketsCounterHub")]
public class MassivePrintHub : Hub
{
public void PostTicketsCount(long count)
{
Clients.All.Send(count);
}
}
public class HubFactory
{
private HubFactory() {}
public static readonly HubFactory Current = new HubFactory();
public IHubProxy GetMassivePrintHubProxy()
{
var hubConnection = new HubConnection(ConfigUtils.GetRequiredSettingValue("adminPath"));
var hubProxy = hubConnection.CreateHubProxy("ticketsCounterHub");
hubConnection.Start().Wait();
return hubProxy;
}
}
Client (JavaScript):
MassivePrintApp.controller("ListController", function ($scope, Dates) {
var hubManager = (function () {
var massivePrintHub = $.connection.ticketsCounterHub;
$.connection.hub.start();
return { massivePrintHub: massivePrintHub };
} ());
hubManager.massivePrintHub.client.Send = function (ticketsCount) {
$scope.action.Quantity = ticketsCount;
$scope.$digest();
};
});
The key part of code is in MVC controller:
public FileResult PrintAction(int actionId, int count, DateTime actionDate, bool isThermo=false)
{
var ticketsCount = _ticketService.GetTicketsInStatusCount(actionId, actionDate, TicketStatusEnum.ToPrint);
HubFactory.Current.GetMassivePrintHubProxy().Invoke("PostTicketsCount", ticketsCount);
var stream = new MemoryStream();
xmlResponse.Save(stream);
stream.Flush();
stream.Position = 0;
return File(stream,ContentTypeEnum.XML.ToString(),String.Format("массовая {0} мероприятия {1} {2}шт.xml", isThermo?"термопечать":"печать", action.Artist,count));
}
As you can see, we have this line:
HubFactory.Current.GetMassivePrintHubProxy().Invoke("PostTicketsCount", ticketsCount);
And that causes the issue, that is whenever we call it one more instance of hub was added to "Requests" section on IIS.
I understand we already started hub in JavaScript code, but I'm not sure how can I use the existing connection or how to get rid of HubFactory or delete created hub instance.
And I don't understand why hub hangs on IIS.
Starting from a more simple example will help you a lot I guess. After that you can look into hosting your SignalR server differently (Console App or Windows Service) the basics won't change
(First installed SignalR: NuGet: install-package Microsoft.AspNet.SignalR)
I made a simple web-app example. The project has a Hub class:
using Microsoft.AspNet.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace SRTest
{
public class MassivePrintHub : Hub
{
private static IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<MassivePrintHub>();
// Can be called from your Javascript code
public void PostTicketsCount(long count)
{
Clients.All.Send(count);
}
// Can be called from your c# code
public static void Static_PostTicketsCount(long count)
{
hubContext.Clients.All.Send(count);
}
}
}
An Owin startup class:
using Microsoft.AspNet.SignalR;
using Microsoft.Owin;
using Owin;
[assembly: OwinStartup(typeof(SRTest.Startup))]
namespace SRTest
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
var hubConfiguration = new HubConfiguration();
hubConfiguration.EnableDetailedErrors = true;
app.MapSignalR(hubConfiguration);
}
}
}
Page (Razor just to be able to call a simulator which calls a c# class to post message from backend):
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>TEST PAGE</title>
<!--Reference the jQuery library. -->
<script src='Scripts/jquery-1.6.4.js'></script>
<!--Reference the SignalR library. -->
<script src='Scripts/jquery.signalR-2.2.0.js'></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="signalr/hubs"></script>
</head>
<body>
THIS IS A TEST PAGE
<!-- Call simulator (trigger event every 5 seconds) -->
#{SRTest.SendFromBackEnd.SimulateSend();}
<script>
$(function () {
var printHub = $.connection.massivePrintHub;
// when send event happens
printHub.client.send = function (count) {
console.log("Send " + count + " tickets");
};
$.connection.hub.start().done(function () {
console.log("Connected");
});
$.connection.hub.logging = true;
});
</script>
</body>
</html>
And I added a dummy class which triggers the event through hubcontext every 5 seconds.
using System.Threading;
using System.Web;
namespace SRTest
{
public class SendFromBackEnd
{
public static void SimulateSend()
{
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
while (true)
{
MassivePrintHub.Static_PostTicketsCount(2);
Thread.Sleep(5000);
}
}).Start();
}
}
}
I added some loggings to the SignalR, add some debug points, it will help you understand the basics, then it will be much easier to build what you are planning to build.
EDIT
About the hanging request: As long as you have a client connected to your SignalR server with SSE or AJAX Long-Polling, you will have an ongoing request, which never finishes. (In case of AJAX Long-polling, it finishes for very short times and comes back). In the apps where I use only Javascript clients, I only see the request if a page is open where I am listening to events. If no page or static page open then no request.
In the apps where I am using .NET clients, as long as the two apps are running, and both Sartup classes executed, the request will always be there, even if no page open. (Since the .NET client is still listening to events.)
For more info: http://hanselminutes.com/291/damian-edwards-explains-the-realtime-web-for-aspnet-with-signalr
This is a Threading related issue. Try like this
Task.Run(() => connection.Start().ContinueWith(task =>
{
.....
})).Wait();

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.

ASP.Net Web Forms SignalR Object has no Method

I added SignalR to an existing ASP.Net 4 Web Forms application. Created a new folder called Hubs and added a Hub like so:
[HubName("UpdatesHub")]
public class UpdatesHub : Hub
{
public void DownloadUpdates()
{
// Code Removed
}
}
Added the RouteTable.Routes.MapHubs(); to Application_Start and added the following to the page:
<script src="/Scripts/jquery-1.9.1.js"></script>
<script src="/Scripts/jquery.signalR-1.0.1.js"></script>
<script src="/signalr/hubs"></script>
<script type="text/javascript">
$(function () {
// Declare a proxy to reference the hub.
var upd = $.connection.UpdatesHub;
// Code Removed
// Start the connection.
$.connection.hub.start().done(function () {
$('#btnDownload').click(function () {
upd.server.DownloadUpdates();
});
});
});
</script>
But whenever I click the button I just get "Uncaught TypeError: Object # has no method 'DownloadUpdates'". I have tried removing and readding signalr via NuGet but can't seem to get this to work, help!
SignalR camelCases the method names on the server. Try checking if downloadUpdates() exists.

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.

Resources