How To Get The Logged In User Count In ASP.NET

Why Do You Need The Current Logged In User Count


Sometimes it's a client requirement to show the current logged in user count in the web application. This feature is frequently used in forums to show how many users are currently browsing the site. This is just a feature to keep the users updated about the current visitors to the forum.

How To Get The Logged In User Count Without SQL In C#


There are few ways you can keep track of it, First one is using the Database where you have to update it according to every user log in and log out and session out. But I will not prefer it to do a database update for a simple task that can be done easily.

I was stuck into this problem and after a few Google search I got the solution that gives me the current logged in user count from active sessions without using any database operation.

For this you need to refer the following namespaces.
using System.Reflection;
using System.Threading.Tasks;
using System.Web.SessionState;
After adding these namespaces add the below method which will retrive the current logged in user count from session, this method uses reflection.
public int CalculateLoggedinUser()
   {
       int loggedinUserCount = 0;

       object obj = typeof(HttpRuntime).GetProperty("CacheInternal", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null, null);

       object[] obj2 = (object[])obj.GetType().GetField("_caches", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(obj);

       for (int i = 0; i < obj2.Length; i++)
       {
           Hashtable c2 = (Hashtable)obj2[i].GetType().GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(obj2[i]);

           foreach (DictionaryEntry entry in c2)
           {
               object o1 = entry.Value.GetType().GetProperty("Value", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(entry.Value, null);

               if (o1.GetType().ToString() == "System.Web.SessionState.InProcSessionState")
               {
                   SessionStateItemCollection sess = (SessionStateItemCollection)o1.GetType().GetField("_sessionItems", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(o1);

                   if (sess != null)
                   {
                       if (sess["User"] != null)
                       {
                           loggedinUserCount++;

                       }
                   }
               }
           }
       }
       return loggedinUserCount;
   }
But always there is a catch in Microsoft products. This method will work perfectly in your local machine but when you host your site in any windows server then this will crash.

In Windows Server you can not read the property "_cache" from "CacheInternal" object so I have modified the code for local and server.
First you have to check if the code is being executed in server or local with the below line of codes

  if(IsLocalIpAddress(HttpContext.Current.Request.UserHostAddress))
       loggedinUserCount = CalculateLoggedinUserForLocal();
  else
       loggedinUserCount = CalculateLoggedinUserForServer();
               
Then this method will actually say you if it is the server or local

public bool IsLocalIpAddress(string host)
    {
        try
        { // get host IP addresses
            IPAddress[] hostIPs = Dns.GetHostAddresses(host);
            // get local IP addresses
            IPAddress[] localIPs = Dns.GetHostAddresses(Dns.GetHostName());

            // test if any host IP equals to any local IP or to localhost
            foreach (IPAddress hostIP in hostIPs)
            {
                // is localhost
                if (IPAddress.IsLoopback(hostIP)) return true;
                // is local address
                foreach (IPAddress localIP in localIPs)
                {
                    if (hostIP.Equals(localIP)) return true;
                }
            }
        }
        catch { 
        }
        return false;
    }
If its local then "CalculateLoggedinUserForLocal()" method will be executed

public int CalculateLoggedinUserForLocal()
    {
        int loggedinUserCount = 0;

        object obj = typeof(HttpRuntime).GetProperty("CacheInternal", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null, null);

        object[] obj2 = (object[])obj.GetType().GetField("_caches", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(obj);

        for (int i = 0; i < obj2.Length; i++)
        {
            Hashtable c2 = (Hashtable)obj2[i].GetType().GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(obj2[i]);

            foreach (DictionaryEntry entry in c2)
            {
                object o1 = entry.Value.GetType().GetProperty("Value", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(entry.Value, null);

                if (o1.GetType().ToString() == "System.Web.SessionState.InProcSessionState")
                {
                    SessionStateItemCollection sess = (SessionStateItemCollection)o1.GetType().GetField("_sessionItems", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(o1);

                    if (sess != null)
                    {
                        if (sess["User"] != null)
                        {
                            loggedinUserCount++;

                        }
                    }
                }
            }
        }
        return loggedinUserCount;
    }
Else if it is server then method will be executed

public int CalculateLoggedinUserForServer()
   {
       int loggedinUserCount = 0;

       object obj = typeof(HttpRuntime).GetProperty("CacheInternal", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null, null);

       Hashtable c2 = (Hashtable)obj.GetType().GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(obj);

       foreach (DictionaryEntry entry in c2)
       {
           object o1 = entry.Value.GetType().GetProperty("Value", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(entry.Value, null);

           if (o1.GetType().ToString() == "System.Web.SessionState.InProcSessionState")
           {
               SessionStateItemCollection sess = (SessionStateItemCollection)o1.GetType().GetField("_sessionItems", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(o1);

               if (sess != null)
               {
                   if (sess["User"] != null)
                   {
                       loggedinUserCount++;
                   }
               }
           }
       }
       return loggedinUserCount;
   }



Now you got the current logged in user by writing just 2-3 methods of not more than 20 lines of code.

Happy Coding...

No comments:

Post a Comment