Thursday, May 23, 2013

SignalR VB.NET Push Notification


SignalR is a Microsoft library  still in pre release.

https://github.com/SignalR/SignalR

With SignalR we can implement the HTML5 WebSocket without worrying that the web server or the browser supports it. We can do this because the library itself will use long polling or classic polling as an alternative.

This technology finds its expression in chat rooms or online games, but we can think about health information systems, such as notifications to clients of alarms related to the results of laboratories (ok, I did not fancy, it's only my job :-) )

In this post we realize a system of push notifications in VB.Net updated to the latest release of SignalR, at least for now.


Step 1


First open visual studio 2012 and select new project: ASP.NET Empty web application.
Install gituhub Package from Package Manager Console

PM> Install-Package Microsoft.AspNet.SignalR


Step 2

Add a new Class to project and create a PersistantConnection:

Imports System.Threading.Tasks

Imports Microsoft.AspNet.SignalR
Public Class NotificationConnection
    Inherits PersistentConnection
    Protected Overrides Function OnConnected(request As IRequest, connectionId As String) As System.Threading.Tasks.Task
        Return MyBase.OnConnected(request, connectionId)
    End Function
End Class

Step 3

Add our class for notifications (for simplicity we create the note with a value and a time):
Imports Microsoft.AspNet.SignalR

Public Class Notification
    Private _ExamValue As Int32
    Private _Data As String
    Public Property ExamValue As Int32
        Get
            Return _ExamValue
        End Get
        Set(value As Int32)
            _ExamValue = value
        End Set
    End Property
    Public Property Data As String
        Get
            Return _Data
        End Get
        Set(value As String)
            _Data = value
        End Set
    End Property
End Class

Step 4


Critical step, the addition of the file Global.asax.vb, it specializes our router, which will do the job right at the time when the client creates a connection to the server

Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
 RouteTable.Routes.MapConnection(Of NotificationConnection("NotificationConnection","/NotificationConnection")

Task.Factory.StartNew(Function()
Dim connectonManager As IConnectionManager =GlobalHost.DependencyResolver.Resolve(Of IConnectionManager()
Dim connection = connectonManager.GetConnectionContext(Of NotificationConnection)()
 While True
                Dim item = New Notification() With { _
                          .Data = DateTime.Now.ToString("hh:mm:ss"), _
                          .ExamValue= CInt(Math.Ceiling(Rnd() * 100)) 
                          '/* For simplicity we simulate the result of examination with a randomic value 
                        }

               connection.Connection.Broadcast(item)

               Thread.Sleep(1000)
  End While

   End Function)
End Sub

Step 5


Now we move on the client and build our html page:

<script src="scripts/jquery-1.6.4.js" type="text/javascript"></script>
<script src="scripts/json2.js" type="text/javascript"></script>
<script src="scripts/jquery.signalR-1.1.1.js" type="text/javascript"></script>
<script type="text/javascript">
    var output;
    $(document).ready(function () {

        output = $("#output");

    });


    var data;
    var options;
  connection = $.connection('/NotificationConnection');
    connection.received(function (dataItem) {
        var newItem = dataItem;
        output.text("Received exam value: " + newItem.Data + "," + newItem.ExamValue);
    });

    connection.start();
</script>
<body>
<h2>Laboratory Exams Monitoring</h2>
<div id="output"></div>
</body>

That's it, from now our good server will update us every second with the value of the exam to be observed, we can think to plot the trend or change the Application_Start() to retrieve values ​​from the laboratory and just update us in case of alert.


10 comments :

  1. Step 4 generates some errors on "NotificationConnection"

    ReplyDelete
  2. I do not have any problems, you can be more accurate so I try to help you?

    ReplyDelete
  3. same with Yücel, i got errors on step 4,

    on this line
    RouteTable.Routes.MapConnection(Of NotificationConnection("NotificationConnection","/NotificationConnection")
    i even try
    RouteTable.Routes.MapConnection(Of NotificationConnection("NotificationConnection"),"/NotificationConnection")

    and i got this error message
    Overload resolution failed because no accessible 'StartNew' can be called with these arguments:
    'Public Function StartNew(Of TResult)(function As System.Func(Of TResult)) As System.Threading.Tasks.Task(Of TResult)': Type 'IConnectionManager' is not defined.
    'Public Function StartNew(Of TResult)(function As System.Func(Of TResult)) As System.Threading.Tasks.Task(Of TResult)': 'Thread' is not declared. It may be inaccessible due to its protection level.
    'Public Function StartNew(Of TResult)(function As System.Func(Of TResult)) As System.Threading.Tasks.Task(Of TResult)': Data type(s) of the type parameter(s) cannot be inferred from these arguments. Specifying the data type(s) explicitly might correct this error.

    'Public Function StartNew(action As System.Action) As System.Threading.Tasks.Task': Type 'IConnectionManager' is not defined.
    'Public Function StartNew(action As System.Action) As System.Threading.Tasks.Task': 'Thread' is not declared. It may be inaccessible due to its protection level.

    can you help me? thanks

    ReplyDelete
    Replies
    1. HI dwarfVampire,
      you have some problems with your IConnectionManager and Threading
      IConnectionManager is in Microsoft.AspNet.SignalR.Infrastructure library

      The correct RouteTable is:

      RouteTable.Routes.MapConnection(Of NotificationConnection)("NotificationConnection", "/NotificationConnection")

      then, there is an arror in my connectionManager declaration, the right one is:

      Dim connectonManager As IConnectionManager = GlobalHost.DependencyResolver.Resolve(Of IConnectionManager)()
      Dim connection = connectonManager.GetConnectionContext(Of NotificationConnection)()

      don't forget to import in your Global_asax class:

      Imports System.Web.Routing
      Imports System.Threading
      Imports System.Threading.Tasks
      Imports System
      Imports System.Web
      Imports Microsoft.AspNet.SignalR
      Imports Microsoft.AspNet.SignalR.Infrastructure
      Imports System.Collections.Generic
      Imports System.Linq
      Imports System.Diagnostics
      Imports Microsoft.AspNet.SignalR.Hosting

      let me know if it works.
      bye

      Delete
  4. I am using VS 2013 and SignalR 2.0. Where I am running into errors is at the line:
    RouteTable.Routes.MapConnection(Of NotificationConnection("NotificationConnection","/NotificationConnection"). The line is not compiling.
    To be honest, I'm not completely sure what this line of code does (obviously mapping to the NotificationConnection class, but not sure why it is set up this way. Any help would be greatly appreciated.

    ReplyDelete
    Replies
    1. Hi Hugh,
      there is an error in step 4, a ")" is missing !!!.

      Wrong:
      RouteTable.Routes.MapConnection(Of NotificationConnection("NotificationConnection","/NotificationConnection")
      Correct:
      RouteTable.Routes.MapConnection(Of NotificationConnection)("NotificationConnection", "/NotificationConnection")

      Delete
    2. also in ConnectionManager declaration a ")" is missing

      Dim connectonManager As IConnectionManager = GlobalHost.DependencyResolver.Resolve(Of IConnectionManager)()

      Delete
  5. Thanks for the response, but I am receiving the error mapconnection is not a member of System.Web.Routing.RouteCollection. I have imported:

    Imports System.Web.SessionState
    Imports System.Web.Routing
    Imports System.Threading
    Imports System.Threading.Tasks
    Imports System
    Imports System.Web
    Imports Microsoft.AspNet.SignalR
    Imports Microsoft.AspNet.SignalR.Infrastructure
    Imports System.Collections.Generic
    Imports System.Linq
    Imports System.Diagnostics
    Imports Microsoft.AspNet.SignalR.Hosting

    Thanks again.

    ReplyDelete
    Replies
    1. Hi Hugh,
      the post is old so it's using an old version of signalR, but don't worry :-)
      To use the new version:

      1.let lose the global.asax file (comments all rows)

      2.add a new class and paste this code:

      Imports Owin
      Imports Microsoft.AspNet.SignalR
      Imports Microsoft.AspNet.SignalR.Infrastructure
      Imports System.Threading
      Imports System.Threading.Tasks



      Public Class Startup
      Public Sub Configuration(app As IAppBuilder)
      app.MapSignalR(Of NotificationConnection)("/NotificationConnection")
      Task.Factory.StartNew(Function()
      Dim connectonManager As IConnectionManager = GlobalHost.DependencyResolver.Resolve(Of IConnectionManager)()
      Dim connection = connectonManager.GetConnectionContext(Of NotificationConnection)()



      While True
      Dim item = New Notification() With { _
      .Data = DateTime.Now.ToString("hh:mm:ss"), _
      .ExamValue = CInt(Math.Ceiling(Rnd() * 100))
      }

      connection.Connection.Broadcast(item)

      Thread.Sleep(1000)
      End While
      End Function)
      End Sub
      End Class

      3. Replace the line in the html page
      scripts/jquery.signalR-1.1.1.js
      with new version
      scripts/jquery.signalR-2.0.0.min.js"

      I tested it and it is ok, I will appreciate if you let me know if everything is ok now

      Delete
  6. Giuseppe,

    First of all, thanks for the help and thank you for providing the most straightforward, sensible example of a SignalR push that I have found. The changes worked perfectly and the project works as it should. To take things a step further, assuming you wanted to create a database push, would you create a SQLDependency in place of the While loop?

    ReplyDelete