Click here to Skip to main content
Click here to Skip to main content

Real Time Web Solution for Chat by MVC SignalR Hub

, 19 Oct 2014 CDDL
Rate this:
Please Sign up or sign in to vote.
This article explains SignalR concept via chat implementation and interaction between client and server visually and involving hub class.

Introduction

Nowadays, due to increase in the amount of information and necessity of achieving data in short time, we need technologies to cover our requirement in this issue. Assume when in stock market prices are changing in each moment, do you think that user should refresh page every moment to inform the last price? Obviously, it is not a reasonable solution for such a problem. Or with increase in producing products and services, we need customer service to help user and buyer, the best and cheaper communication is conversation by chat program. By the same token, we cannot force user to press button for receiving our last message.

SignalR is a real time technology which is using the set of asynchrony library to make a persistence connection between client and server. User can receive last update data from server without traditional way such as refresh page or press button.

Background

You need to know MVC 4.0 Technology and EntityFramework > 4.0  to get this article better. I recommend this link: {MVC Tutorial: http://technical.cosmicverse.info/Article/Index/3  } and { Data Access Layer Tutorial: http://technical.cosmicverse.info/Article/Index/4 } to learn step by step about MVC and Entity.

In the other hand SignalR uses the below approaches to establish real time web:

1. WebSocket

Websocket is a full duplex protocol and uses http handshaking internally and allow stream of messages flow on top of TCP. It supports: Google Chrome (> 16) Fire Fox (> 11) IE (> 10) Win IIS (>8.0). Due to encrypt message and full duplex, websocket is the best solution and at first signalR checks both web server and client server whether they support websocket or not.

Simplex Communication

It just spreads in one way when one point just broadcasts while another point just can listen without sending message, such as television and radio.

Half duplex

One point sends message and in this moment another point cannot send message and should wait until the first point finishes its transmission and then send its message, it is just a one communication line at a time, such as old wireless device walkie-talkie and HTTP protocol.

Full duplex

Both points can send and receive message at a time simultaneously, there is no need to wait until other point finishes its transmission such as telephones and websocket protocol.

Full Duplex

2. Server Sent Events (SSE)

The next choice for signalr is server sent event, because of persistence communication between server and client. In this approach, communication does not disconnect and last data from server will update automatically and transmit to client via HTTP connection. EventSource is part of HTML5 technology.

     var evsrc = new EventSource("url");
            // Load and Register Event Handler for Messages in this section

            evsrc.addEventListener("message", function (event) {
                //processing data in this section
            });

3. Forever Frame

When client sends request to server, then server sends a hidden iframe as chunked block to client so this iframe is responsible to keep connection between client and server forever. Whenever server changes data, then send data as script tag to client (hidden iframe) and these scripts will be received sequentially.

4. Polling

Client sends request to server and server responses immediately but after that, server disconnects connection so again for establishing communication between server and client, we should wait for next request from client. To solve this problem, we have to set timeout manually and for each 10 seconds client sends request to server to check new modification in server side and gets last update data. Polling uses resources and it is not an economic solution.

5. Long Polling

Client sends request to server and server responds immediately and this connection remains until a specific time and during this period clients do not have to send explicit request to server while in polling client has to send explicit request to server during timeout. Comet programming covers this concept.

Briefly SignalR library chooses one type of transmit data between client and server, its priority is websocket, server sent event, long polling and forever iframe. There are two classes inside this library as follows:

1. Persistentconnection

It is low level so it is complex and needs more configuration but in return gives more facility to handle class personally.

2. Hub

It is high level and more popular to use it.

How to implement simple chat scenario by the aid of signalr and hub class?

My aim is just to issue a random scenario for involving signalr. You can use it for your personally scenario and I just follow the below steps to make challenge with server (hub class) and client side and illustrate how client send request and server respond? How they interact with each other?

Scenario Description

I want to establish an application for customer service department. There are some administrations that are responsible to help client and on the other side there are clients who ask question and need help.

Assume two admins are online and connect to chat service and the first client comes to ask a question, so system connects the first client to first free admin and for second client this story will repeat, but third client gives alarm from system that there is no admin to help. Whenever first client disconnects, the first admin becomes free.

My contract for this scenario is to use flag for reminding which user who is connected is user or admin and which one is free or busy. In my database, if admincode is equal to zero so it is user otherwise it is admin, and I define flag “tpflag” (in application) is equal to zero for user and equal to one for admin. Whenever they connect to chat flag “freeflag” becomes zero which shows busy user and as soon as client leaves conversation becomes one which shows free status.

if freeflag==0 ==> Busy
if freeflag==1 ==> Free
if tpflag==0 ==> User
if tpflag==1 ==> Admin

Prerequisites
  1. Visual Studio 2012
  2. SQL server 2008
  3. Install necessary dependency from package manager console

Step 1: Create Project

File --> New Project --> ASP.Net MVC 4 Web Application { Give Name and Directory} --> { Template=Basic & View Engine=Razor }

Step 2: Open PM for Installing Dependency Files

Menu (Tools) --> Library Package Manager --> Package Manager Console

Step 3: Instruction for Removing Old Dependency

At first, remove all of the old decencies for installing new version of SignalR 2.x.x In Line:

PM> Uninstall-Package Microsoft.AspNet.SignalR –RemoveDependencies

Step 4: Instruction for Installing Necessary Dependency Files

For new version, use:

PM> Install-Package Microsoft.AspNet.SignalR

I have used signalr version 2.0.1 for this practice:

PM> Install-Package Microsoft.AspNet.SignalR -Version 2.0.1

PM> Install-Package Microsoft.Owin

By writing this instruction, nuget does all of the dependency injection that you need to run signalr. If you look at Reference part in solution, you will fine Microsoft.ASPNet.SignalR.x, Microsoft.Owin.x.x.. and etc, or if you look at Scripts part in solution jquery-1.x , jquery.signalR 2.x.x and etc. so feel comfort about all of dependency.

Solution --> Open Reference -->

Solution --> Scripts -->

On the other hand, after installing signalR dependencies successfully, you will find complete help as readme.txt above the package console. It contains all the necessary instructions to get started with signalr. I explain these instructions in the next steps.

Tips (1): NuGet

If you encounter this error “The remote name could not be resolved: 'www.nuget.org'” So you should change Package Manager Settings which is located in front of the Package Source.

You should change source from https to http protocol to solve this problem.

Tips (2): Owin

Check your references part to be sure there is Owin, otherwise follow this instruction: Right Click on References --> Manage NuGet Packages --> Select Online in left side --> search Owin --> Select Owin (Owin IAppBuilder startup interface) --> Install.

Then you should see Owin in your reference part.

Step 5: Startup Class

For enabling signalr in project, you should create class as startup. (If in previous version of signalr , I mean first version, you used to write RouteTable.Routes.MapHubs(); in Application Start in global.asax, now forget about it and just use startup class. Right Click: On Project Name {SignalR} --> Add Class --> Name: Startup.cs

using Microsoft.Owin;
using Owin;

[assembly: OwinStartup(typeof(MvcSignal.Startup))]
namespace MvcSignal
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.MapSignalR();
        }
    }
}

Step 6: Organize Database According To Our Scenario

Step 6.1: Create “tbl_User”

tbl_user” will collect user and admin, if “AdminCode” was filled by number from previous table so it is admin who belongs to department otherwise if it was filled by (zero) illustrate to ordinary user.{ “UserID” int + identity=yes and “AdminCode” default value = 0 }

Step 6.2: Create “tbl_Conversation”

tbl_Conversation” which will collect data from conversation between user and admin. This table will be filled after finishing conversation. { “ConID” int + identity=yes }

Step 7: Create Hub class

Step 7.1: Model (folder) --> Create class “UserInfo.cs”

public class UserInfo
    {
        public string ConnectionId { get; set; }
        public string UserName { get; set; }
        public string UserGroup { get; set; }

        //if freeflag==0 ==> Busy
        //if freeflag==1 ==> Free
        public string freeflag { get; set; }

        //if tpflag==2 ==> User Admin
        //if tpflag==0 ==> User Member
        //if tpflag==1 ==> Admin

        public string tpflag { get; set; }

        public int UserID { get; set; }
        public int AdminID { get; set; } 
    }

Step 7.2: Model (folder) --> Create class “MessageInfo.cs”

  public class MessageInfo
    {
        public string UserName { get; set; }

        public string Message { get; set; }

        public string UserGroup { get; set; }

        public string StartTime { get; set; }

        public string EndTime { get; set; }

        public string MsgDate { get; set; }
    }

Step 7.3: Controller (folder) ? Create “HomeController.cs”

     public ActionResult Chat()
        {
            ViewBag.Message = "Your contact page.";

            return View();
        }

Right click on Chat() --> Select Add View -->

Step 7.4: Create Chat.cshtml { Client Side }

@{
    ViewBag.Title = "Chat";
}

<div id="divLogin" class="mylogin">

    User Name:<input id="txtUserName" type="text" /><br />
       Password :   <input id="txtPassword" type="password" /><br />
    <input id="btnLogin" type="button" value="Login" />
    <div id="divalarm"></div>
</div>

<div id="divChat" class="mylogin">

<div id="welcome"></div><br />
<input id="txtMessage" type="text" />
<input id="btnSendMessage" type="button" value="Send" />
<div id="divMessage"></div>

</div>

    <input id="hUserId" type="hidden" />
    <input id="hId" type="hidden" />
    <input id="hUserName" type="hidden" />
    <input id="hGroup" type="hidden" />

@section scripts {
   
    <script src="~/Scripts/jquery-1.8.2.min.js"></script>
    <script src="~/Scripts/jquery.signalR-2.0.1.min.js" type="text/javascript"></script>
    <script src="~/signalr/hubs" type="text/javascript"></script>
    @*<script type="text/javascript" src="@Url.Content("~/signalr/hubs")"></script>*@
    @* <script type="text/javascript" src='<%= ResolveClientUrl("~/signalr/hubs") %>'></script>*@
   
   <script>
       $(function () { //This section will run whenever we call Chat.cshtml page

           $("#divChat").hide();
           $("#divLogin").show();

           var objHub = $.connection.myHub;

           loadClientMethods(objHub);

           $.connection.hub.start().done(function () {

               loadEvents(objHub);

           });
       });

       function loadEvents(objHub) {

           $("#btnLogin").click(function () {

               var name = $("#txtUserName").val();
               var pass = $("#txtPassword").val();

               if (name.length > 0 && pass.length > 0) {
                   // <<<<<-- ***** Return to Server [  Connect  ] *****
                   objHub.server.connect(name, pass);

               }
               else {
                   alert("Please Insert UserName and Password");
               }

           });

           $('#btnSendMessage').click(function () {

               var msg = $("#txtMessage").val();

               if (msg.length > 0) {

                   var userName = $('#hUserName').val();
                   // <<<<<-- ***** Return to Server [  SendMessageToGroup  ] *****
                   objHub.server.sendMessageToGroup(userName, msg);

               }
           });

           $("#txtPassword").keypress(function (e) {
               if (e.which == 13) {
                   $("#btnLogin").click();
               }
           });

           $("#txtMessage").keypress(function (e) {
               if (e.which == 13) {
                   $('#btnSendMessage').click();
               }
           });
       }

       function loadClientMethods(objHub) {

           objHub.client.NoExistAdmin = function () {
               var divNoExist = $('<div><p>There is no Admin to response you try again later</P></div>');
               $("#divChat").hide();
               $("#divLogin").show();

               $(divNoExist).hide();
               $('#divalarm').prepend(divNoExist);
               $(divNoExist).fadeIn(900).delay(9000).fadeOut(900);
           }

           objHub.client.getMessages = function (userName, message) {

               $("#txtMessage").val('');
               $('#divMessage').append('<div><p>' + userName + ': ' + message + '</p></div>');

               var height = $('#divMessage')[0].scrollHeight;
               $('#divMessage').scrollTop(height);
           }

           objHub.client.onConnected = function (id, userName, UserID, userGroup) {

               var strWelcome = 'Welcome' + +userName;
               $('#welcome').append('<div><p>Welcome:' + userName + '</p></div>');

               $('#hId').val(id);
               $('#hUserId').val(UserID);
               $('#hUserName').val(userName);
               $('#hGroup').val(userGroup);

               $("#divChat").show();
               $("#divLogin").hide();
           }
       }
    </script>
}

Step 7.5: Create Model1.edmx

To have a simple way to fetch and insert data from and to database, I create model as follows: Right click on project name --> Add New Item --> Select “ADO.NET Entity Data Model” --> Select “Generate From Data Base” --> Make Connection to your data base --> Select your tables.

Step 8: Create folder and name it Hubs then create simple class and name it “MyHub.cs”

{If you have the last update version of Visual Studio, you can add new item and select “SignalR Hub Class”}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.AspNet.SignalR;
using MvcSignal.Models;
using Microsoft.AspNet.SignalR.Hubs;

namespace MvcSignal
{
    public class MyHub : Hub
    {
        static List UsersList = new List();
        static List<messageinfo> MessageList = new List<messageinfo>();

        //-->>>>> ***** Receive Request From Client [  Connect  ] *****
        public void Connect(string userName, string password)
        {
            var id = Context.ConnectionId;
            string userGroup="";
            //Manage Hub Class
            //if freeflag==0 ==> Busy
            //if freeflag==1 ==> Free

            //if tpflag==0 ==> User
            //if tpflag==1 ==> Admin


            var ctx = new TestEntities();

            var userInfo =
                 (from m in ctx.tbl_User
                  where m.UserName == userName && m.Password == password
                  select new { m.UserID, m.UserName, m.AdminCode }).FirstOrDefault();

            try
            {
                //You can check if user or admin did not login before by below line which is an if condition
                //if (UsersList.Count(x => x.ConnectionId == id) == 0)

                //Here you check if there is no userGroup which is same DepID --> this is User otherwise this is Admin
                //userGroup = DepID
               
               
                if ((int)userInfo.AdminCode == 0)
                {
                    //now we encounter ordinary user which needs userGroup and at this step, 
                    //system assigns the first of free Admin among UsersList
                    var strg = (from s in UsersList where (s.tpflag == "1") 
                    && (s.freeflag == "1") select s).First();
                    userGroup = strg.UserGroup;

                    //Admin becomes busy so we assign zero to freeflag which is shown admin is busy
                    strg.freeflag = "0";

                    //now add USER to UsersList
                    UsersList.Add(new UserInfo { ConnectionId = id, 
                                                 UserID = userInfo.UserID, 
                                                 UserName = userName, 
                                                 UserGroup = userGroup, 
                                                 freeflag = "0", 
                                                 tpflag = "0", });
                    //whether it is Admin or User now both of them has userGroup and I Join this user or admin to specific group 
                    Groups.Add(Context.ConnectionId, userGroup);
                    Clients.Caller.onConnected(id, userName, userInfo.UserID, userGroup);
                }
                else
                {
                    //If user has admin code so admin code is same userGroup
                    //now add ADMIN to UsersList
                    UsersList.Add(new UserInfo { ConnectionId = id, 
                                                 AdminID = userInfo.UserID, 
                                                 UserName = userName, 
                                                 UserGroup = userInfo.AdminCode.ToString(), 
                                                 freeflag = "1", 
                                                 tpflag = "1" });
                    //whether it is Admin or User now both of them has userGroup and I Join this user or admin to specific group 
                    Groups.Add(Context.ConnectionId, userInfo.AdminCode.ToString());
                    Clients.Caller.onConnected(id, userName, userInfo.UserID, userInfo.AdminCode.ToString());
                }                       
            }

            catch
            {
                string msg = "All Administrators are busy, please be patient and try again";
                //***** Return to Client *****
                Clients.Caller.NoExistAdmin();
            }
        }
        // <<<<<-- ***** Return to Client [  NoExist  ] *****

        //--group ***** Receive Request From Client [  SendMessageToGroup  ] *****
        public void SendMessageToGroup(string userName, string message)
        {
            if (UsersList.Count != 0)
            {
                var strg = (from s in UsersList where (s.UserName == userName) select s).First();
                MessageList.Add(new MessageInfo 
                { UserName = userName, Message = message, UserGroup = strg.UserGroup });
                string strgroup = strg.UserGroup;
                // If you want to Broadcast message to all UsersList use below line
                // Clients.All.getMessages(userName, message);

                //If you want to establish peer to peer connection use below line 
                //so message will be send just for user and admin who are in same group
                //***** Return to Client *****
                Clients.Group(strgroup).getMessages(userName, message);
            }
        }
        // <<<<<-- ***** Return to Client [  getMessages  ] *****

        //--group ***** Receive Request From Client ***** 
        //{ Whenever User close session then OnDisconneced will be occurs }
        public override System.Threading.Tasks.Task OnDisconnected()
        {

            var item = UsersList.FirstOrDefault(x => x.ConnectionId == Context.ConnectionId);
            if (item != null)
            {
                UsersList.Remove(item);

                var id = Context.ConnectionId;

                if (item.tpflag == "0")
                {
                    //user logged off == user
                    try
                    {
                        var stradmin = (from s in UsersList where 
                        (s.UserGroup == item.UserGroup) && (s.tpflag == "1") select s).First();
                        //become free
                        stradmin.freeflag = "1";
                    }
                    catch
                    {
                        //***** Return to Client *****
                        Clients.Caller.NoExistAdmin();
                    }                    
                }

                //save conversation to dat abase
            }

            return base.OnDisconnected();
        }
    }
}</messageinfo>

Rules and Contracts

The prefix when client wants to call server method in server side:

( Client --> Server ) // Client send request to server

1. objHub.server.methodname() { methodname of server side }

and there is exactly the same methodname in server side (myHub.cs class) .

The prefix when server wants to call client method in client side:

( Server --> Client ) // Server calls client method & { methodname of client side }

  1. Clients.caller.methodname() // caller means only user who send request

  2. Clients.all.methodname() // all means all of connected user

  3. Clients.Group(groupName).methodname() // Group means just users who are in same group

When there is ***** Return to Client ***** in “MyHub.cs” class, it means you have to write jquery function with the same name in client side.

Indeed their interaction is such as follow:

Tips (3): Call Server Class

There are tiny tips whenever you want to call your server class; always in client side you should use specific naming convention which is camel type, for instance if your hub class name is “MyHub”, you should instantiate your object from “myHub” or if you have “SendMessageToGroup”, you should call it from “sendMessageToGroup” so it should be like:

Test Case

To have the same result, you should have database as same as I have explained in the seventh step.

Case 1

Test Plan: If Client tries to login and there is no admin, then system shows an alarm.

Testing Steps
  1. Run project
  2. UserName: mahsa
  3. Password :123
  4. Expected Output: System shows alarm

Case 2

Test Plan: There is at least on free admin and then one client login and then the first admin will be assigned to the first free client who needs help.

Testing Steps

  1. Run project
  2. UserName: admin1
  3. Password :123
  4. Login {admin1 as first admin}

  5. Copy URL to another web browser

  6. UserName: mahsa
  7. Password :123

  8. Login {mahsa as first client}
  9. If “mahsa” send message, so “admin1” will see it, because they are in same group. When the first client true to login then add to the first free admin.

  10. Copy URL to another web browser
  11. UserName: kashi
  12. Password :123

  13. Login { kashi as second client}
  14. System shows alarm and says “there is no admin then system shows an alarm”

  15. Copy URL to another web browser
  16. UserName: admin2
  17. Password :123
  18. “kashi” and “admin2” cannot see conversation between “admin1” and “mahsa” 

MVC Tutorial: http://technical.cosmicverse.info/Article/Index/3 

Data Access Layer Tutorial: http://technical.cosmicverse.info/Article/Index/4 

 

 

License

This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)

Share

About the Author

Mahsa Hassankashi
Software Developer
Iran (Islamic Republic Of) Iran (Islamic Republic Of)
I have been working with .Net framework for 7 years.
I`d like to challenge with complex problem, then make it easy for using everyone. This is the best joy.
 
ICT Master student at University of Agder
-------------------------------------------------------------
Diamond is nothing except the pieces of the coal which have continued their activities finally they have become Diamond.
 
http://www.cosmicverse.info/
http://www.technical.cosmicverse.info
Follow on   Google+   LinkedIn

Comments and Discussions

 
QuestionMy vote of 5 PinprofessionalSibeesh KV17-Nov-14 18:31 
AnswerRe: My vote of 5 PinmemberMahsa Hassankashi17-Nov-14 23:29 
GeneralRe: My vote of 5 PinprofessionalSibeesh KV17-Nov-14 23:40 
GeneralMy vote of 5 PinprofessionalManoj Kr. Verma13-Nov-14 2:00 
GeneralRe: My vote of 5 PinmemberMahsa Hassankashi13-Nov-14 2:03 
GeneralMy vote of 5 PinmemberHumayun Kabir Mamun20-Oct-14 2:15 
GeneralRe: My vote of 5 PinmemberMahsa Hassankashi20-Oct-14 15:41 
QuestionNice article but I have a question... PinprofessionalSuvabrata Roy19-Oct-14 20:29 
AnswerRe: Nice article but I have a question... PinmemberMahsa Hassankashi20-Oct-14 15:40 
GeneralRe: Nice article but I have a question... PinprofessionalSuvabrata Roy20-Oct-14 19:18 
Questiondeja vu PinprofessionalRavi Kant Srivastava16-Sep-14 2:06 
AnswerRe: deja vu PinmemberMahsa Hassankashi16-Sep-14 6:41 
GeneralMy vote of 5 PinmemberHumayun Kabir Mamun15-Sep-14 21:22 
GeneralRe: My vote of 5 PinmemberMahsa Hassankashi16-Sep-14 6:33 
QuestionHow can I create group for specific users (UserProfile)? PinmemberNMathur26-Aug-14 1:24 
AnswerRe: How can I create group for specific users (UserProfile)? PinmemberMahsa Hassankashi27-Aug-14 2:18 
GeneralRe: How can I create group for specific users (UserProfile)? PinprofessionalNMathur9-Sep-14 2:48 
QuestionGreat job! PinmemberJohn S Mangam12-Aug-14 10:11 
AnswerRe: Great job! PinmemberMahsa Hassankashi12-Aug-14 11:48 
GeneralRe: Great job! PinmemberJohn S Mangam12-Aug-14 20:45 
GeneralRe: Great job! PinmemberMahsa Hassankashi12-Aug-14 23:32 
GeneralRe: Great job! PinmemberJohn S Mangam13-Aug-14 7:39 
GeneralRe: Great job! PinmemberMahsa Hassankashi16-Sep-14 6:43 
QuestionFetching realtime data PinmemberKamal Kahar6-Aug-14 22:51 
AnswerRe: Fetching realtime data PinmemberMahsa Hassankashi8-Aug-14 12:23 
QuestionHow to make it between users PinmemberRama Sagar Pulidindi23-Jul-14 1:27 
AnswerRe: How to make it between users PinmemberMahsa Hassankashi8-Aug-14 12:25 
BugThe type or namespace name 'TestEntities' could not be found PinmemberMember 1095855220-Jul-14 2:32 
GeneralRe: The type or namespace name 'TestEntities' could not be found PinmemberMahsa Hassankashi20-Jul-14 10:42 
GeneralRe: The type or namespace name 'TestEntities' could not be found PinmemberMember 1095855220-Jul-14 11:28 
GeneralRe: The type or namespace name 'TestEntities' could not be found PinmemberMahsa Hassankashi20-Oct-14 15:44 
QuestionHow to show Users who are online-----Signal R PinmemberRama Sagar Pulidindi15-Jul-14 0:41 
AnswerRe: How to show Users who are online-----Signal R PinmemberMahsa Hassankashi15-Jul-14 7:54 
QuestionHow to call the singler Method form Console/WIn appliaction PinmemberMember 36667148-Jul-14 3:16 
Questionalhamdullilah nice article PinmemberMember 36667148-Jul-14 3:11 
AnswerRe: alhamdullilah nice article PinmemberMahsa Hassankashi8-Aug-14 12:27 
Questiontbl_Conversation PinmemberLuis Carlos Infante1-Jul-14 6:22 
AnswerRe: tbl_Conversation PinmemberMahsa Hassankashi2-Jul-14 7:32 
QuestionWhat is better PinmemberLuisCordovaLeon6-Jun-14 7:09 
AnswerRe: What is better PinmemberMahsa Hassankashi2-Jul-14 7:30 
QuestionI don't quite understand. You have two definitions for your table "tbl_user" PinmemberDanielBar28-May-14 9:13 
AnswerRe: I don't quite understand. You have two definitions for your table "tbl_user" PinmemberMahsa Hassankashi6-Jun-14 22:24 
Generalnice article PinmemberMember 1049604117-Apr-14 9:44 
QuestionThanks! PinmemberMember 107189062-Apr-14 6:42 
AnswerRe: Thanks! PinmemberMahsa Hassankashi14-Apr-14 10:36 
GeneralMy vote of 5 PinprofessionalRenju Vinod19-Mar-14 21:14 
GeneralRe: My vote of 5 PinmemberMahsa Hassankashi24-Mar-14 5:57 
QuestionUrgent : How to maintain SignalR client connection id on page refresh. PinmemberNajmul Hoda3-Mar-14 10:17 
AnswerRe: Urgent : How to maintain SignalR client connection id on page refresh. PinmemberMahsa Hassankashi4-Mar-14 0:19 
GeneralRe: Urgent : How to maintain SignalR client connection id on page refresh. PinmemberNajmul Hoda29-Oct-14 14:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.1411022.1 | Last Updated 19 Oct 2014
Article Copyright 2014 by Mahsa Hassankashi
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid