In this recipe, we are going to take a look at a common task for most sites. We want to be able to keep track of how many users are present on our site at any given time. We will also attempt to distinguish between known (logged-in users) and unknown (anonymous) users. To keep track of users as they come to our site, we will need to attach some of the events, such as Session_Start
and Session_End
, which our application exposes. To distinguish between the known and unknown users, we will have to tap into our membership functionalities, such as registration, logging on, and logging off.
SiteVisitors
and place it in the Models
directory. The SiteVisitors
class will provide us with the ability to keep track of how many visitors of each type are on our site at a given time. As we only ever want one instance of this class to exist at any one time during the lifetime of our application, we will use the singleton pattern to control the number of instances that are created. In its most simple form, the singleton pattern requires us to have a private constructor (so that no one can instantiate an instance of the class directly) and a static method, which will return an instance of the class to us. In this static method, we will check if we have created an instance or not and behave accordingly.Models/SiteVisitors.cs:
public class SiteVisitors { private static SiteVisitors instance = new SiteVisitors(); private SiteVisitors() { } public int UnknownVisitorCount { get; private set; } public int KnownVisitorCount { get; private set; } public static SiteVisitors Instance { get { return instance; } } }
Enum
that will provide a method for us to identify the two different types of users—known and unknown. We will do this by creating a new Enum
in the Models
folder called VisitorTypes.cs
.Models/VisitorTypes.cs:
public enum VisitorTypes { UnknownVisitor = 0, KnownVisitor = 1 }
VisitorType
. Rather than working directly with the session all over our application, we will create a wrapper class to handle this functionality for us. Therefore, we need to create a new class called SessionWrapper.cs
in the Models
folder. This class will have two methods—one method each to set the two different types into a user's session depending on who we think they are at the time, and another method to get the user's current type.public static class SessionWrapper { private const string c_visitorType = "VisitorType"; public static void SetUnknownType() { HttpContext.Current.Session.Add(c_visitorType, VisitorTypes.UnknownVisitor); } public static void SetKnownType() { HttpContext.Current.Session.Add(c_visitorType, VisitorTypes.KnownVisitor); } public static new VisitorTypes GetType() { VisitorTypes result = VisitorTypes.UnknownVisitor; if (HttpContext.Current.Session[c_visitorType] != null) result = (VisitorTypes)HttpContext.Current.Session [c_visitorType]; return result; } }
SiteVisitors
class to provide a bit more functionality. Currently, we are able to get an instance of the SiteVisitors
class on which we have two properties from which we can read values. We now need to expose some helper methods that allow our application to toggle certain actions as an unknown user becomes known, a user leaves the site, and so on.public class SiteVisitors { ... //user arrives public void UnknownVisitorArrived() { SessionWrapper.SetUnknownType(); UnknownVisitorCount++; } //known user arrives public void KnownVisitorArrives() { SessionWrapper.SetKnownType(); KnownVisitorCount++; } //user logs in public void UnknownVisitorLoggedOn() { SessionWrapper.SetKnownType(); UnknownVisitorCount--; KnownVisitorCount++; } //user logs out public void KnownVisitorLoggedOut() { SessionWrapper.SetUnknownType(); logged-in userstrackingUnknownVisitorCount++; KnownVisitorCount--; } //user session expires public void VisitorLeft() { if (SessionWrapper.GetType() == VisitorTypes.KnownVisitor) KnownVisitorLeft(); else UnknownVisitorLeft(); } //anon user session expires private void UnknownVisitorLeft() { UnknownVisitorCount--; } //logged in user session expires private void KnownVisitorLeft() { KnownVisitorCount--; } }
Global.asax
. We need to increment our user count when a user comes to the site. We also need to decrement our counts when a user's session expires.Keep in mind that when a user leaves the site, we won't be informed immediately. The session timeout is a value generally set by the web server. Usually, this value is set for 20 minutes. This means that you won't know a user left until 20 minutes after they actually left.
protected void Session_Start() { if(Request.IsAuthenticated) SiteVisitors.Instance.KnownVisitorArrives(); else SiteVisitors.Instance.UnknownVisitorArrived(); } protected void Session_End() { SiteVisitors.Instance.VisitorLeft(); }
AccountModels.cs
file (Models folder). In there, you will find a few classes and interfaces. Look for the FormsAuthenticationService
. This is where the SignIn
and SignOut
functions can be attached to. For each of those methods, we need to move some of our numbers around, either from known to unknown or unknown to known.public class FormsAuthenticationService : IFormsAuthenticationService { public void SignIn(string userName, bool createPersistentCookie) { if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName"); FormsAuthentication.SetAuthCookie(userName, createPersistentCookie); SiteVisitors.Instance.UnknownVisitorLoggedOn(); } public void SignOut() { FormsAuthentication.SignOut(); SiteVisitors.Instance.KnownVisitorLoggedOut(); } }
Site.Master
page and adding to our display for all pages in one shot.<div id="logindisplay"> <% Html.RenderPartial("LogOnUserControl"); %><br /> Known visitor count: <%: StateExamples.Models.SiteVisitors.Instance .KnownVisitorCount %> - Unknown visitor count: <%: StateExamples.Models.SiteVisitors.Instance.UnknownVisitorCount %> </div>
This recipe takes advantage of our application events, session, and the fact that the static instance we get from our singleton class stays accessible during the entire lifetime of the application. With these components, we are able to increment and decrement users from our counts, keep track of the state of users as they move around on our site, and display some stats to our users.