DNN 8 (DotNetNuke) How to configure SignalR? - signalr

For one of my projects I have to use DNN.
I created a regular ASP.NET project in which I experimented with SignalR and it works perfect.
But within DNN version 8, after installing the SignalR NuGet packages both Core and for JS and creating the startup class such as this:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
A testhub class such as this:
public class MyTestHub : Hub
{
public void Hello(string message)
{
//Clients.All.hello();
// set all clients
var clients = Clients.All;
// call javascript function
clients.test("This is a test");
Trace.WriteLine(message);
}
}
and finally an index page:
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
DNN 8 SignalR TestPage
</div>
</body>
</html>
<script src="~/DesktopModules/MVC/AC_ChatTest1/Scripts/jquery.signalR-2.4.1.min.js"></script>
<script src="~/signalr/hubs"></script>
<script>
$(function () {
console.log('in on ready');
// set up the hub connection
var hub = $.connection.mytest;
console.log(hub);
// define JS function that is called from
hub.client.test = function (msg) {
console.log(msg);
}
$.connection.hub
.start()
.done(function () {
hub.server.hello("SignalR is working.");
})
});
</script>
I get this error:

Are you doing this in the context of a DNN module?
If so, you should take a look at this: https://www.chrishammond.com/Blog/itemid/2624/using-signalr-with-dotnetnuke-modules. Chris' module is available on GitHub, too.
If not, you should switch to a module and take a serious approach with Chris' solution.

I figured this out, these are the steps needed to make signalR work in DNN 7.1+:
Install NuGet Packages.
Create a startup class like this:
[assembly: OwinStartup(typeof(Startup))]
namespace MyNamespace.MyModuleName
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
}
To eliminate the error localhost/signalr/hubs (404 not found) Need to modify dbo.hostsettings table in DNN so run this query:
IF NOT EXISTS (SELECT * FROM dbo.hostsettings WHERE SettingName =
'AUM_DoNotRewriteRegEx' )
insert into dbo.hostsettings
(SettingName
, SettingValue
, SettingIsSecure
, CreatedByUserId
, CreatedOnDate
, LastModifiedByUserId
, LastModifiedOnDate
)
values(
'AUM_DoNotRewriteRegEx'
,'/DesktopModules/|/Providers|/LinkClick\.aspx|/SignalR'
, 0
, -1
, GETDATE()
, -1
, GETDATE()
)
GO
IF EXISTS (SELECT * FROM dbo.hostsettings WHERE SettingName = 'AUM_DoNotRewriteRegEx'
and SettingValue not like '%/signalr%' )
update dbo.hostsettings
set settingValue = (select settingValue + '|/signalr' from dbo.hostsettings where
settingname = 'AUM_DoNotRewriteRegEx')
where settingname = 'AUM_DoNotRewriteRegEx'
GO

I think this:
// set up the hub connection
var hub = $.connection.mytest;
Should be:
// set up the hub connection
var hub = $.connection.myTestHub;

Related

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();

Self hosted SignalR method not called

I am new to SignalR and having a bit of difficulty using a SignalR hub in a self hosted scenario.
At the moment (just for testing) I have the simplest hub possible:
public class NotificationsHub : Hub
{
public void Hello(string name)
{
Clients.All.hello("Hello " + name);
}
}
This hub class is in a Class Library project which is referenced in a Windows Service application. I've added all the nuget packages to the Windows Service app and added the OWIN Startup class which looks like this:
public class Startup
{
public void Configuration(IAppBuilder app)
{
AppDomain.CurrentDomain.Load(typeof(NotificationsHub).Assembly.FullName); // as selfhosting doesn't scan referenced libraries for Hubs
app.Map("/signalr", map => {
map.UseCors(CorsOptions.AllowAll);
var hubConfig = new HubConfiguration {
EnableDetailedErrors = true,
EnableJSONP = true
};
map.RunSignalR(hubConfig);
});
}
}
In the Windows Service OnStart method I host the signalR using:
SignalR = WebApp.Start<Startup>("http://*:9191/");
In the ASP.NET MVC app which should interact with SignalR I have the following code:
<script src="Scripts/jquery.signalR-2.1.1.min.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="http://localhost:9191/signalr/hubs"></script>
<script type="text/javascript">
$(function () {
$.connection.hub.url = "http://localhost:9191/signalr";
var notif = $.connection.notificationsHub;
notif.client.hello = function (msg)
{
alert(msg);
}
$.connection.hub.start().done(function () {
$("#sayHello").click(function () {
notif.server.hello($("#myName").val());
});
});
});
</script>
The problem is this doesn't work in my correct setup... in the browser console I have no errors, the ~/signalr/hubs js looks fine...
If I do more or less the same configuration, but host the SignalR in the ASP.NET MVC app, everything works as expected.
UPDATE: following #halter73 's suggestion to enable the client side logging for the hub, I've got the following error message which I still can't fix:
SignalR: notificationshub.Hello failed to execute. Error: Method not
found: 'Microsoft.AspNet.SignalR.Hubs.IHubCallerConnectionContext
Microsoft.AspNet.SignalR.Hub.get_Clients()'.
Could somebody please let me know what I am missing?
Thank you in advance!
Andrei
Since 2.1.0, get_Clients should return a IHubCallerConnectionContext<dynamic> instead of IHubCallerConnectionContext.
The error you are seeing could happen if you compile your application against SignalR <= 2.03 but loading SignalR >= 2.10 at runtime.

MVC 4 java script error unable to get property 'Taskhub'

I tried to display signal r messages on my index page. following hub details
public class TasksHub : Hub
{
public void NewTaskDetails(int taskid,string taskname,string created,string role)
{
Clients.All.addMessage(taskid, taskname, created, role);
}
}
my error is : Unhandled exception at line 60, column 9 in http://localhost:45845/
0x800a138f-JavaScript runtime error:
Unable to get property TasksHub of undefined or null reference
<script src="~/Scripts/jquery-1.8.2.js"></script>
<script src="~/Scripts/jquery.signalR-1.1.0.js"></script>
<script src="~/signalr/hubs"></script>
<script type="text/javascript">
$(function ()
{
var THub = $.connection.TasksHub;
$messages = $("#messages");
THub.client.send = function (taskid, taskname, created, role) {
$messages.append("<br /><b>" + taskid + ":</b>" + taskname);
}
$.connection.THub.start();
});
use Camel Casing
For Example if Class name MyChatHub
then in you should call
var chat = $.connection.myChatHub;
Hope it helps and make sure all script files are loaded and check in Global.asax file
added
RouteTable.Routes.MapHubs();

Self host SignalR with Cross domain ASP.Net Client callback fail

I have a WPF application which use SignalR to achieve publish/subscribe model.
When I used a WPF client to connect to the above application, the publish and callback worked successfully.
Then I created a ASP.Net client. I use a cross domain property of SignalR to connect to above WPF application.
It could connect to the application and call the method provided in the hub successfully.
However, when the WPF application call the method in the ASP.Net Client, it seems that that call cannot be reached to the client browser
(viewed in Firefox, the long polling does not return; break point cannot be reached even I have set the break point in the javascript callback function, and nothing could be displayed in the broswer).
I have included the following script in html
<script src="#Url.Content("~/Scripts/jquery-1.6.4.min.js")" type="text/javascript</script>
<script src="/Scripts/jquery.signalR-1.0.0-rc2.min.js" type="text/javascript"></script>
<script src="http://localhost:9999/signalr/hubs" type="text/javascript"></script>
The following is the javascript that I have used.
jQuery.support.cors = true;
myHub = $.connection.subscriberHub;
myHub.client.addMessage = function (msg, time) {
$("#message").prepend("<div>" + time + " " + msg + "</div>");
};
$.connection.hub.url = 'http://localhost:9999/signalr';
$.connection.hub.start();
The below is the server code in the WPF application:
public partial class App : Application
{
private IDisposable app;
private void Application_Startup(object sender, StartupEventArgs e)
{
string url = "http://localhost:9999";
app = WebApplication.Start<Startup>(url);
}
private void Application_Exit(object sender, ExitEventArgs e)
{
if (app != null)
{
app.Dispose();
}
}
}
class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapHubs();
}
}
And I send the message when the WPF application clicked a button:
private void btn_sendMsg_Click(object sender, RoutedEventArgs e)
{
var context = GlobalHost.ConnectionManager.GetHubContext<SubscriberHub>();
DateTime sentTime = DateTime.Now;
context.Clients.Group("subscriber").addMessage(tb_message.Text, sentTime);
MessageList.Insert(0,string.Format("{0:yyyy-MM-dd HH:mm:ss} {1}", sentTime, tb_message.Text));
}
The following is the hub that I have defined:
public class SubscriberHub : Hub
{
string group = "subscriber";
public Task Subscribe()
{
return Groups.Add(Context.ConnectionId, group);
}
public Task Unsubscribe()
{
return Groups.Remove(Context.ConnectionId, group);
}
}
Is there any problem in the above code?
Don't you need to have the JavaScript client call Subscribe?
$.connection.hub.start().done(function () {
myHub.server.subscribe();
});
Alternatively you could modify SubscriberHub:
public class SubscriberHub : Hub
{
string group = "subscriber";
public override Task OnConnected()
{
return Groups.Add(Context.ConnectionId, group);
}
// ...
}

SignalR Error 500 when calling server from client in ASP.NET 4.5 Website

I'm using SignalR with ASP.NET 4.5 webforms. I wasn't able to make the client talk to the server and vice versa. What I want to achieve is simply being able to to test how the Client can trigger a server function and how the server can trigger a client function. Here's the code I use:
Client Side Code (HitCounter.aspx)
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script type="text/javascript" src="Scripts/jquery-1.8.2.js"></script>
<script src="Scripts/jquery.signalR-0.5.3.min.js"></script>
<script type="text/javascript" src="SignalR/Hubs"></script>
<style>
#currentHitCount {
font-family: Arial;
font-size:40pt;
margin-left:auto;
margin-right:auto;
display:block;
text-align:center;
}
</style>
</head>
<body>
<div id="currentHitCount"></div>
<script type="text/javascript">
$(function () {
var hub = $.connection.hitCounter;
$.extend(hub, {
showHitCount: function (hitCount){
if(hitCount > 1){
$('#currentHitCount')
.html("This site has had " + hitCount + " hits.");
}
else{
$('#currentHitCount')
.html("This site has had " + hitCount + " hit.");
}
},
addMessage: function (str) {
$('#currentHitCount')
.html("Getting Message: " + str);
}
});
$.connection.hub.start(function () {
hub.addMessage("test");
hub.addHit();
});
$.connection.hub.stateChanged(function (change) {
if ($.signalR.connectionState["connected"] === change.newState) {
}
});
$.connection.hub.error(function () {
});
});
</script>
<div style="background-color:red; width:290px; height:200px; color:white; position:absolute; top:100px; left:30px;" id="thebutton">test</div>
</body>
</html>
Server Side Code (App_Code/HitCounterHub.cs)
using SignalR.Hubs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
[HubName("hitCounter")]
public class HitCounterHub : Hub, IDisconnect
{
static int _hitCount;
public void addHit(string pageId)
{
_hitCount += 1;
Clients.showHitCount(_hitCount);
}
public Task ClientCallingServer()
{
_hitCount += 1;
return Clients["foo"].showHitCount(_hitCount);
}
public Task Join()
{
return Groups.Add(Context.ConnectionId, "foo");
}
public Task Send(string message)
{
_hitCount += 1;
return Clients["foo"].addMessage(message);
}
public Task Disconnect()
{
return Clients["foo"].leave(Context.ConnectionId);
}
}
I am running the code locally on IIS7.5 but I get and error when running the code in Visual Studio 2012 too.
The Error:
localhost/signalr/signalr/send?transport=serverSentEvents&connectionId=400f1302-6c8e-418e-a14c-da95f836a29d
500 (Internal Server Error)
Using Chrome debugger the error page shows:
'addHit' method could not be resolved.
Again, what I am trying to do is to make a simple test to check how to call a server from the client and the client from the server.
Thanks.
The reason why you're not able to resolve your addHit method is because you have the same function name on the server as you do the client.
AKA To call the server you're doing hub.addHit("laksjdf"). But to call the client it'd be the same thing: hub.addHit("laksjdfldksj"); Hence there's ambiguity. Change the name of either your client or server side functions so they're unique in their naming sense.
This issue will be fixed in the next release of SignalR.

Resources