List all active ASP.NET Sessions
Asked Answered
S

9

50

How can I list (and iterate through) all current ASP.NET sessions?

Silent answered 24/9, 2009 at 8:4 Comment(2)
Possible duplicate of Get a list of all active sessions in ASP.NETFredrick
In SQL Server Management Studio, the command is exec sp_who.Septicemia
D
36

You can collect data about sessions in global.asax events Session_Start and Session_End (only in in-proc settings):

private static readonly List<string> _sessions = new List<string>();
private static readonly object padlock = new object();

 public static List<string> Sessions
 {
       get
       {
            return _sessions;
       }
  }

  protected void Session_Start(object sender, EventArgs e)
  {
      lock (padlock)
      {
          _sessions.Add(Session.SessionID);
      }
  }
  protected void Session_End(object sender, EventArgs e)
  {
      lock (padlock)
      {
          _sessions.Remove(Session.SessionID);
      }
  }

You should consider use some of concurrent collections to lower the synchronization overhead. ConcurrentBag or ConcurrentDictionary. Or ImmutableList

https://msdn.microsoft.com/en-us/library/dd997373(v=vs.110).aspx

https://msdn.microsoft.com/en-us/library/dn467185.aspx

Disbursement answered 24/9, 2009 at 9:13 Comment(4)
This will only work for InProc sessions as all other session types won't raise the Session_End() event.Blanchette
Not only won't raise Session_End, if you have a webfarm for load balancing the session will only be added on the current server.Pled
locking at the property is wrong for many reasons. initialise to new list in a static constructor or inline with the field. the locking should go around the add and remove and also any enumerations, or use a synchronised collectionMalcommalcontent
Thanks a lot for the feedback guys. The answer was quite old anyway, so I updated it little bit.Disbursement
M
20

http://weblogs.asp.net/imranbaloch/archive/2010/04/05/reading-all-users-session.aspx

How this works :

InProc session data is stored in the HttpRuntime’s internal cache in an implementation of ISessionStateItemCollection that implements ICollection. In this code, first of all i got CacheInternal Static Property of HttpRuntime class and then with the help of this object i got _entries private member which is of type ICollection. Then simply enumerate this dictionary and only take object of type System.Web.SessionState.InProcSessionState and finaly got SessionStateItemCollection for each user.

Summary :

In this article, I show you how you can get all current user Sessions. However one thing you will find when executing this code is that it will not show the current user Session which is set in the current request context because Session will be saved after all the Page Events...

Mantoman answered 16/4, 2011 at 10:15 Comment(2)
fantastic work by imran. I have question, how do I get sessions id value instead of sessions valuesNighthawk
The method used in this article will only work in a full trust environment. This worked fine in development and failed quickly in production.Bronnie
S
20

It does not seems right that there is not any class or method that provides this information. I think, it is a nice to have feature for SessionStateStoreProvider, to have a method which returns current active session, so that we don't have to actively track session life in session_start and session_end as mention by Jan Remunda.

Since I could not find any out of box method to get all session list, And I did not wanted to track session life as mentioned by Jan, I end up with this solution, which worked in my case.

public static IEnumerable<SessionStateItemCollection> GetActiveSessions()
{
    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)
                {
                    yield return sess;
                }
            }
        }
    }
}
Sedan answered 14/3, 2012 at 16:47 Comment(2)
@pilavdzice: If you have SQLServer sessions, then the task is trivial. Just query the session table and serialize the session object(s).Ectoparasite
In SQL Server Management Studio, the command to access sessions is exec sp_who. dataedo.com/kb/query/sql-server/list-database-sessionsSepticemia
H
7

I've been looking around for an equivalent of @ajitdh's answer for later versions of ASP.net - couldn't find anything, so thought I'd update this thread with solution for v4.6.2... Haven't tested with later versions of .net, but it doesn't work with v4.5. I am guessing it will be compatible with v4.6.1 onward.

Cache cache = HttpRuntime.Cache;
MethodInfo method = typeof( System.Web.Caching.Cache ).GetMethod( "GetInternalCache", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy );

object objx = method.Invoke( cache, new object[] { false } );

FieldInfo field = objx.GetType().GetField( "_cacheInternal", BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.Instance );
objx = field.GetValue( objx );

field = objx.GetType().GetField( "_cachesRefs", BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.Instance );
objx = field.GetValue( objx );

IList cacherefs = ( (IList)objx );
foreach( object cacheref in cacherefs )
{
    PropertyInfo prop = cacheref.GetType().GetProperty( "Target", BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance );

    object y = prop.GetValue( cacheref );

    field = y.GetType().GetField( "_entries", BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.Instance );

    Hashtable c2 = (Hashtable)field.GetValue( y );
    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 )
            {
                // Do your stuff with the session!
            }
        }
    }
}
Heintz answered 9/11, 2018 at 13:3 Comment(1)
This should be the accepted answer now. The older solution can't work at all in newer versions of .Net.Phosphoric
S
6

I really like ajitdh's answer. Do upvote him. Here's another reference to that solution:

http://weblogs.asp.net/imranbaloch/reading-all-users-session

This got me close, but it failed to achieve my personal goal of finding the session for a particular session Id that I knew. So, for my purposes, I just added the sessionid as a session item (say Session["SessionId"] = session.SessionId on Session Start.) Then I just looked for a session with a matching value... I would have preferred to actually pull out this entry by indexing into one of the collections, but this did get it working at least.

Of course, this is all for In-Proc sessions, which I am indeed considering moving away from.

private static SessionStateItemCollection GetSession(string sessionId)
{
    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 o0 = entry.Value.GetType().GetProperty("Value", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(entry.Key, null);
            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["SessionId"] != null && ((string)sess["SessionId"]) == sessionId)
                    {
                        return sess;
                    }
                }
            }
        }
    }

    return null;
}
Summerlin answered 9/3, 2015 at 13:19 Comment(0)
B
2

Here's a WebForms/Identity example that gets a list of the logged on users active within the last 30 minutes.

The answer below works for a single web server, and the cache will be lost if the application restarts. If you want to persist the data and share it between servers in a web farm, this answer might be of interest.

In Global.asa.cs:

public static ActiveUsersCache ActiveUsersCache { get; } = new ActiveUsersCache();

and

protected void Application_PreRequestHandlerExecute(object sender, EventArgs e)
{
    if (User != null && User.Identity.IsAuthenticated)
    {
        // Only update when the request is for an .aspx page
        if (Context.Handler is System.Web.UI.Page)
        {
            ActiveUsersCache.AddOrUpdate(User.Identity.Name);
        }
    }
}

The add these couple of classes:

public class ActiveUsersCache
{
    private readonly object padlock = new object();
    private readonly Dictionary<string, DateTime> cache = new Dictionary<string, DateTime>();
    private DateTime lastCleanedAt;

    public int ActivePeriodInMinutes { get; } = 30;
    private const int MinutesBetweenCacheClean = 30;

    public List<ActiveUser> GetActiveUsers()
    {
        CleanCache();

        var result = new List<ActiveUser>();
        lock (padlock)
        {
            result.AddRange(cache.Select(activeUser => new ActiveUser {Name = activeUser.Key, LastActive = activeUser.Value}));
        }
        return result;
    }

    private void CleanCache()
    {
        lastCleanedAt = DateTime.Now;
        var cutoffTime = DateTime.Now - TimeSpan.FromMinutes(ActivePeriodInMinutes);
        lock (padlock)
        {
            var expiredNames = cache.Where(au => au.Value < cutoffTime).Select(au => au.Key).ToList();
            foreach (var name in expiredNames)
            {
                cache.Remove(name);
            }
        }
    }

    public void AddOrUpdate(string userName)
    {
        lock (padlock)
        {
            cache[userName] = DateTime.Now;
        }

        if (IsTimeForCacheCleaup())
        {
            CleanCache();
        }
    }

    private bool IsTimeForCacheCleaup()
    {
        return lastCleanedAt < DateTime.Now - TimeSpan.FromMinutes(MinutesBetweenCacheClean);
    }
}

public class ActiveUser
{
    public string Name { get; set; }

    public DateTime LastActive { get; set; }

    public string LastActiveDescription
    {
        get
        {
            var timeSpan = DateTime.Now - LastActive;
            if (timeSpan.Minutes == 0 && timeSpan.Seconds == 0)
                return "Just now";
            if (timeSpan.Minutes == 0)
                return timeSpan.Seconds + "s ago";
            return $"{timeSpan.Minutes}m  {timeSpan.Seconds}s ago";
        }
    }
}

Finally, in the page where you want to display the active users, display the results:

UserRepeater.DataSource = Global
    .ActiveUsersCache.GetActiveUsers().OrderByDescending(u => u.LastActive);
UserRepeater.DataBind();
Benis answered 21/6, 2017 at 6:1 Comment(0)
M
-1

To the best of my knowledge, with standard in-memory sessions you cannot. This is something I was grappling with last week and I came to the determination that it isn't possible unless you are using a session state server. Seems rather odd from a design standpoint if you ask me. :/

Mishap answered 24/9, 2009 at 8:17 Comment(0)
P
-5

you can also try this as something basic, this is only so you can understand the concept

protected void Page_Load(object sender, EventArgs e)
    {
        if (Convert.ToInt32(Session["islogged"]) == 0)
        {
            Label1.Text = "you are not logged";
        }

    }

    protected void logged(object sender, EventArgs e)

/* add it to a button so the looged in button when it is clicked this will appear*/ {

            Label1.Text = "you are  Logged in "; 


    }
Preheat answered 8/10, 2018 at 19:20 Comment(0)
O
-18

Try the following code

 for (int i = 0; i < Session.Keys.Count - 1; i++)
        {
            Label1.Text += Session.Keys.Get(i) + " - " + Session[i].ToString()+"<br/>";
        }
Ornithischian answered 24/9, 2009 at 8:24 Comment(1)
That's an iteration across the current session's keys. I want to iterate and display data from all sessions currently active within the server's session state.Silent

© 2022 - 2024 — McMap. All rights reserved.