Jorg Ancrath
Jorg Ancrath

Reputation: 1447

asp.net based chat - where to go from here?

I'm building a very simple chat page on my website, here is what I have so far:

chat

And this is the code:

<asp:Content ID="Content2" ContentPlaceHolderID="maincontent" Runat="Server">
    <script language="javascript" type="text/javascript">
        var prm = Sys.WebForms.PageRequestManager.getInstance();
        prm.add_endRequest(EndRequestHandler);

        function EndRequestHandler(sender, args) {
            document.getElementById('<%=FormView1.FindControl("chattxt").ClientID%>').focus(); 
        }
    </script>
    <div id="chatbg">
    <asp:UpdatePanel ID="UpdatePanel1" runat="server">
    <ContentTemplate>
<div id="chatmessages">
    <asp:Repeater ID="Repeater1" runat="server" DataSourceID="SqlDataSource1">
    <ItemTemplate>
        <asp:Literal ID="Literal1" runat="server" Text='<%# Eval("Username") %>'></asp:Literal>: <asp:Literal ID="Literal2" runat="server" Text='<%# Eval("Message") %>'></asp:Literal><br />
    </ItemTemplate>
    </asp:Repeater>
</div>
    <asp:ListBox ID="ListBox1" runat="server" Rows="16" CssClass="memberlist"></asp:ListBox>
    </ContentTemplate>
    </asp:UpdatePanel>
        <asp:UpdatePanel ID="UpdatePanel2" runat="server" UpdateMode="Conditional">
        <ContentTemplate>
        <asp:FormView ID="FormView1" runat="server" DefaultMode="Insert"
        OnItemInserted="fv_ItemInserted" RenderOuterTable="False" 
            DataSourceID="SqlDataSource1">
        <InsertItemTemplate>
            <asp:Panel ID="Panel1" runat="server" DefaultButton="Button1">
            <asp:TextBox ID="chattxt" runat="server" CssClass="chattxtbox"
            Text='<%# Bind("Message") %>' autocomplete="off"></asp:TextBox>
        <asp:Button ID="Button1" runat="server" CommandName="insert" style="display:none" Text="Button"/>
            </asp:Panel>
        </InsertItemTemplate>
        </asp:FormView>
        </ContentTemplate>
        </asp:UpdatePanel>
</div>
    <asp:SqlDataSource ID="SqlDataSource1" runat="server" 
        ConnectionString="<%$ ConnectionStrings:orangefreshConnectionString1 %>" 
        InsertCommand="INSERT INTO [Chat] ([Username], [Message]) VALUES (@Username, @Message)" 
        SelectCommand="SELECT [Id], [Username], [Message], [Date] FROM [Chat] ORDER BY [Id]" >
        <InsertParameters>
            <asp:Parameter Name="Message" Type="String" />
            <asp:Parameter Name="Date" Type="DateTime" /> 
            <asp:SessionParameter Name="Username" SessionField="Username" Type="String" />
        </InsertParameters>
    </asp:SqlDataSource>
    <script type="text/javascript">
        function scrollpos() {
            var objDiv = document.getElementById("chatmessages");
            objDiv.scrollTop = objDiv.scrollHeight;
        }


</script>
</asp:Content>

This is my codebehind:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Security;
using orangefreshModel;

public partial class chat : BasePage
{

    protected void Page_Load(object sender, EventArgs e)
    {
        Session["Username"] = User.Identity.Name;
        ListBox1.Items.Clear();
        foreach (MembershipUser myuser in Membership.GetAllUsers())
            if (myuser.IsOnline == true)
            {
                ListBox1.Items.Add(myuser.ToString());

            }

        ScriptManager.RegisterStartupScript(
                            UpdatePanel1,
                            this.GetType(),
                            "ScrollPos",
                            "scrollpos();",
                            true);
    }

    protected void fv_ItemInserted(object sender, FormViewInsertedEventArgs e)
    {
        this.FormView1.DataBind();
    }
}

So all of this is working pretty good, the UpdatePanel ensures my messages popup straight away, the problem now is seeing other user messages. This is my first ever ASP.NET project, so I thought about cheesing it a bit and trying a Timer every second to repopulate my repeater... but to no success, and I don't think it's a wise solution at all:

First of all, the Timer affecting my Repeater window made it impossible for a user to scroll back up to read messages, seeing as it refreshed every second scrolling was just not possible.

Secondly, I never got it to work anyway, this is the event I tried for my Timer:

 protected void Timer1_Tick(object sender, EventArgs e)
        {
            this.Repeater1.DataSource = SqlDataSource1;
            this.Repeater1.DataBind();
        }

But yeah, no success.

This is where I'm at right now with this chat: I can see my messages popping up fine, but now I need to figure out a way for my window to update as other users type their message, if some other user types a new message in, I need to send in my own message in order to see theirs.

I've been browsing the web for a bit, read a few things about Signal R, but I was wondering if someone had simpler idea on where I should go with this, how to implement real time updates on my message window?

Upvotes: 0

Views: 304

Answers (4)

nunespascal
nunespascal

Reputation: 17724

SignalR is a good option.

However if you do want to do it yourself for some reason, this is how you should go about it.

The most efficient method to build a chat in asp.net is to use a IHttpAsyncHandler and ajax requests.

An async request allows you to delay the response of a request till an external event occurs.
A user makes a call to this handler and waits till someone sends him a message.
Messages are delivered as soon as you send them to a user.

On receiving a message the client makes another request and waits for the next message. This is how you keep a persistent connection open and allows the server to push data to the client.

This is a lot more efficient than polling the site to check if messages have arrived.
Using the async handler also ensures that no asp.net threads are wasted while a user waits for messages to come. Polling with a timer you will quickly run out of threads to process requests in asp.net.

This ensures that your chat can scale well even as the number of users of the site goes up.

Here is a completely working project that implements this, along with ajax.

Upvotes: 1

Dave
Dave

Reputation: 583

I agree with KingPancake that SignalR would be a very good way to approach something like this. You could "back-end" this with something like the new .Net Web API in conjunction with SignalR if you want to persist the data at all

Check out this presentation by Brad Wilson which might help you:

Upvotes: 1

AndyC
AndyC

Reputation: 1335

I suggest looking into SignalR - http://nuget.org/packages/signalr

SignalR is a library that keeps persistent connection open and allows the server to "push" data to the client.

You can also look at http://www.slideshare.net/adammokan/introduction-to-signalr-10082193 as a decent overview of SignalR

It

Upvotes: 2

cjk
cjk

Reputation: 46415

One option is to do this outside of the ASP.Net lifecycle and makea manual AJAX request to the server. The server method can hold on to the connection for a max of 30 secs, returning any new messages as soon as they arrive. This is then returned to the client and handled in JS, and they then open another connection and wait again. I've heard this called the reverse AJAX poll.

However you detect new messages is up to you, you could poll the DB every second, or maintain in memory message etc.

Note that this can cause problems if thousands of clients are holding open connections to the server.

Upvotes: 1

Related Questions