How can I list (and iterate through) all current ASP.NET sessions?
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 cache = new Dictionary();
private DateTime lastCleanedAt;
public int ActivePeriodInMinutes { get; } = 30;
private const int MinutesBetweenCacheClean = 30;
public List GetActiveUsers()
{
CleanCache();
var result = new List();
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();