views:

442

answers:

3

I built an ajax chat in one of my mvc website. everything is working fine. I am using polling. At certain interval i am using $.post to get the messages from the db. But there is a problem. The message retrieved using $.post keeps on repeating. here is my javascript code and controller method.

 var t;        
        function GetMessages() {        
            var LastMsgRec = $("#hdnLastMsgRec").val();
            var RoomId = $("#hdnRoomId").val();
            //Get all the messages associated with this roomId
            $.post("/Chat/GetMessages", { roomId: RoomId, lastRecMsg: LastMsgRec }, function(Data) {
                if (Data.Messages.length != 0) {
                    $("#messagesCont").append(Data.Messages);
                    if (Data.newUser.length != 0)
                        $("#usersUl").append(Data.newUser);
                    $("#messagesCont").attr({ scrollTop: $("#messagesCont").attr("scrollHeight") - $('#messagesCont').height() });
                    $("#userListCont").attr({ scrollTop: $("#userListCont").attr("scrollHeight") - $('#userListCont').height() });
                }
                else {
                }
                $("#hdnLastMsgRec").val(Data.LastMsgRec);
            }, "json");


            t = setTimeout("GetMessages()", 3000);
        }

and here is my controller method to get the data:

public JsonResult GetMessages(int roomId,DateTime lastRecMsg)
        {
            StringBuilder messagesSb = new StringBuilder();
            StringBuilder newUserSb = new StringBuilder();            
            List<Message> msgs = (dc.Messages).Where(m => m.RoomID == roomId && m.TimeStamp > lastRecMsg).ToList();
            if (msgs.Count == 0)
            {
                return Json(new { Messages = "", LastMsgRec = System.DateTime.Now.ToString() });
            }            
            foreach (Message item in msgs)
            {
                messagesSb.Append(string.Format(messageTemplate,item.User.Username,item.Text));
                if (item.Text == "Just logged in!")
                    newUserSb.Append(string.Format(newUserTemplate,item.User.Username));
            }            

            return Json(new {Messages = messagesSb.ToString(),LastMsgRec = System.DateTime.Now.ToString(),newUser = newUserSb.ToString().Length == 0 ?"":newUserSb.ToString()});
        }

Everything is working absloutely perfect. But i some messages getting repeated. The first time page loads i am retrieving the data and call GetMessages() function. I am loading the value of field hdnLastMsgRec the first time page loads and after the value for this field are set by the javascript.

I think the message keeps on repeating because of asynchronous calls. I don't know, may be you guys can help me solve this.

or you can suggest better way to implement this.

A: 

By default $.post() caches the results

You can either call $.ajaxSetup ({ cache: false}); before JS GetMessages function call to ensure caching is disabled or change the $.post to $.ajax and set cache attribute to false. In the end $.post() is a short cut to this:

$.ajax({
  type: 'POST',
  url: url,
  data: data,
  success: success
  dataType: dataType
});
kaivalya
+1  A: 

Kaivalya is correct about the caching, but I'd also suggest that your design could and should be altered just a tad.

I made a very similar app recently, and what I found was that my design was greatly enhanced by letting the controllers work with the fairly standard PRG pattern (post-redirect-get). Why enhanced? well, because POST methods are built to add stuff to an app, GET methods are supposed to be used to get information without side effects. Your polling should be just getting new messages w/o side effects.

So rather than your $.post call expecting data and handling the callback, what I'd recommend is having your controller expose a method for creating new chat messages via POST and then another method that get the last X chat messages, or the messages since a certain timestamp or whatever.

The javascript callback from the post action, then can update some variables (e.g. the last message id, timestamp of the last message, or even the whole URL of the next message based on the info contained in a redirect, whatever).

The $.post would fire only in response to user input (e..g type in a box, hit 'send') Then, you have (separately) a $.get call from jquery that's set up to poll like you said, and all it does is fetch the latest chat messages and it's callback updates the chat UI with them.

Paul
A: 

I got my answer here: ASP.NET AJAX CHAT

The names below i am referring to are from above link.

i think the actual problem was with the timestamp thing and asynchronous behaviour of $.post. after calling "GetMessages()" method, even if the previous request to retrive chat message was not complete anathor call to same method used to fire due to setting timeout for "GetMessages()" method outside the $.post method. In my question you can see that timeout for "GetMessages()" method is set outside the $.post method. Now i set the timeout for "GetMessages()" method inside the $.post method. so that next call to "GetMessages()" only occur after 3 seconds of completion of current $.post method. I have posted the code below.

var t;
        function GetMessages() {
            var LastMsgRec = $("#hdnLastMsgRec").val();
            var RoomId = $("#hdnRoomId").val();
            //Get all the messages associated with this roomId
            $.post("/Chat/GetMessages", { roomId: RoomId, lastRecMsg: LastMsgRec }, function(Data) {
                if (Data.LastMsgRec.length != 0)
                    $("#hdnLastMsgRec").val(Data.LastMsgRec);
                if (Data.Messages.length != 0) {
                    $("#messagesCont").append(Data.Messages);
                    if (Data.newUser.length != 0)
                        $("#usersUl").append(Data.newUser);
                    $("#messagesCont").attr({ scrollTop: $("#messagesCont").attr("scrollHeight") - $('#messagesCont').height() });
                    $("#userListCont").attr({ scrollTop: $("#userListCont").attr("scrollHeight") - $('#userListCont').height() });
                }
                else {
                }
                t = setTimeout("GetMessages()", 3000);
            }, "json");

        }

I addition to that i also changed few things. As suggested by ignatandrei i placed $("#hdnLastMsgRec").val(Data.LastMsgRec); immediately after function(Data) {.

and also

as said by MikeSW i changed the data retrieval process. Previously i was extracting data on the basis of timespan(retrieve all the data associated with this room id that has greater timespan than last data retrieved message timespan) but now i keep track of the messageid. Now i retrieve only those data that has message id greater than last retrieved message id.

and guess what no repeataion and perfectly working chat application so far on my intranet.

I still got to see it's performance when deployed on internet.

i think it solved my problem.

i will still test the system and let u guys know if there is any problem.

nccsbim071