Screenshot of a conversation using the chat
Introduction
I will show you how to build a Web-based chat using ASP.NET 2.0/3.5, ScriptServices and a SQL Server database, that can handle several requests and simultaneous users.
The source provided is pretty much ready to be copied and pasted into any 3.5 web application.
Requirements
- The chat application must be HTTP-based. No other protocol allowed.
- The application must allow multiple chat rooms.
- The user can leave the room without notifying the application.
- The list of chatters in the room must reflect the latest changes with a delay not greater than (for example) 5 seconds.
- The messages list must be retrieved with a delay not greater than (for example) 2 seconds.
Application State
As in HTTP the connection is closed after a single request/response pair, you have to simulate the status of being connected to the chat room. I accomplish that by having an application state object of the chat users. Also, as you don't have a notification when the user is disconnected, you will have to check regularly for the latest activity from the user in order to manually remove it from the chat users list.
public void ValidateUsers(TimeSpan maxInterval)
{
List<int> toDelete = new List<int>();
foreach (System.Collections.Generic.KeyValuePair<int> keyValue in this.Users)
{
if (DateTime.Now.Subtract(keyValue.Value.LastActivity) > maxInterval)
{
toDelete.Add(keyValue.Key);
}
}
}
Ajax Enabled Webservice
The Ajax service exposes four methods:
EnterRoom
: It assigns the user to the chat room by adding the user to the room users list. CheckMessages
: It's responsible to get the latest message from the database and check the users list. SendMessage
: It saves the message and checks the users list, returning the latest messages. CheckUsers
: It validates all the users list from the chat room by getting the latest activity from the users and, if the list changed, returns the new users list.
Client Scripting
The client script is responsible to refresh the list of messages on the screen by using setTimeout
to Ajax calls.
Here you can see the JavaScript that makes the request to the webservice and its callbacks.
Codeproject.Chat.EnterRoom = function()
{
SampleChat.Chat.Services.ChatService.EnterRoom(Codeproject.Chat.RoomId,
Codeproject.Chat.EnterRoomCallback);
}
Codeproject.Chat.EnterRoomCallback = function(lastMessageId)
{
Codeproject.Chat.LastMessageId = lastMessageId;
Codeproject.Chat.MessagePanel.className = "";
Codeproject.Chat.CheckUsers();
Codeproject.Chat.CheckMessages();
}
Codeproject.Chat.CheckUsers = function ()
{
SampleChat.Chat.Services.ChatService.CheckUsers(Codeproject.Chat.CheckUsersCallback);
setTimeout(Codeproject.Chat.CheckUsers, Codeproject.Chat.CheckUsersRefresh);
}
Codeproject.Chat.CheckUsersCallback = function(response)
{
if (response.Users.length > 0)
{
Codeproject.Chat.ArrangeUsers(response.Users);
}
}
Codeproject.Chat.CheckMessages = function ()
{
SampleChat.Chat.Services.ChatService.CheckMessages(Codeproject.Chat.LastMessageId,
Codeproject.Chat.CheckMessagesCallback);
setTimeout(Codeproject.Chat.CheckMessages, Codeproject.Chat.CheckMessagesRefresh);
}
Codeproject.Chat.CheckMessagesCallback = function(response)
{
if (response.Messages.length > 0)
{
Codeproject.Chat.LastMessageId = response.LastMessageId
Codeproject.Chat.ArrangeMessages(response.Messages);
}
if (response.Users.length > 0)
{
Codeproject.Chat.ArrangeUsers(response.Users);
}
}
Codeproject.Chat.SendMessage = function ()
{
var message = Codeproject.Chat.MessageTextbox.value;
if (message.trim() != "")
{
SampleChat.Chat.Services.ChatService.SendMessage(message,
Codeproject.Chat.LastMessageId,
Codeproject.Chat.CheckMessagesCallback);
Codeproject.Chat.MessageTextbox.focus();
Codeproject.Chat.MessageTextbox.value = "";
}
}
All the client script needed to run the chat is provided in the source under the namespace: Codeproject.Chat
.
Database
Here is the design of the database table used by the chat.
As the amount of records may grow a lot in a few hours/days, it's very important to query this table through its clustered index, getting only the new messages since the previous message you retrieved (storing in the user state the Id
of the last message retrieved).
CREATE TABLE [dbo].[ChatMessages](
[MessageId] [int] IDENTITY(1,1) NOT NULL,
[RoomId] [int] NOT NULL,
[MessageBody] [varchar](250) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[MessageDate] [datetime] NOT NULL,
[UserId] [int] NOT NULL,
[IsSystem] [bit] NOT NULL,
CONSTRAINT [PK_ChatMessages] PRIMARY KEY CLUSTERED
(
[MessageId] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
Hope you enjoy it!
History
- March 2nd, 2009 - Article submitted
- March 9th, 2009 - Article body extended
- February 24th, 2010 - JS improved