【问题标题】:List all active ASP.NET Sessions列出所有活动的 ASP.NET 会话
【发布时间】:2010-12-01 00:25:11
【问题描述】:

如何列出(并遍历)所有当前的 ASP.NET 会话?

【问题讨论】:

标签: asp.net session session-state


【解决方案1】:

您可以在 global.asax 事件 Session_Start 和 Session_End 中收集有关会话的数据(仅在进程内设置中):

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);
      }
  }

您应该考虑使用一些并发集合来降低同步开销。 ConcurrentBag 或 ConcurrentDictionary。或者不可变列表

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

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

【讨论】:

  • 这仅适用于 InProc 会话,因为所有其他会话类型都不会引发 Session_End() 事件。
  • 不仅不会引发 Session_End,如果你有一个用于负载平衡的 webfarm,会话只会添加到当前服务器上。
  • 锁定属性是错误的,原因有很多。在静态构造函数中初始化为新列表或与字段内联。锁定应该围绕添加和删除以及任何枚举,或使用同步集合
  • 非常感谢大家的反馈。反正答案很老了,所以我更新了一点。
【解决方案2】:

似乎没有任何类或方法提供此信息是不对的。我认为,拥有 SessionStateStoreProvider 的功能是一个很好的功能,有一个返回当前活动会话的方法,这样我们就不必像 Jan Remunda 提到的那样在 session_start 和 session_end 中主动跟踪会话生命。

由于我找不到任何开箱即用的方法来获取所有会话列表,并且我不想像 Jan 提到的那样跟踪会话生命,所以我最终得到了这个解决方案,它在我的情况下有效。

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;
                }
            }
        }
    }
}

【讨论】:

【解决方案3】:

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

这是如何工作的:

InProc 会话数据存储在实现 ICollection 的 ISessionStateItemCollection 实现中的 HttpRuntime 内部缓存中。在这段代码中,首先我得到了 HttpRuntime 类的 CacheInternal 静态属性,然后在这个对象的帮助下,我得到了 ICollection 类型的 _entries 私有成员。然后简单地枚举这个字典,只取 System.Web.SessionState.InProcSessionState 类型的对象,最后得到每个用户的 SessionStateItemCollection。

总结:

在本文中,我将向您展示如何获取所有当前用户会话。但是在执行此代码时您会发现一件事是它不会显示在当前请求上下文中设置的当前用户 Session,因为 Session 将在所有页面事件之后保存...

【讨论】:

  • imran 的出色作品。我有疑问,如何获取会话 ID 值而不是会话值
  • 本文使用的方法只适用于完全信任的环境。这在开发中运行良好,但在生产中很快失败。
【解决方案4】:

我真的很喜欢 ajitdh 的回答。给他点赞。这是对该解决方案的另一个参考:

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

这让我很接近,但它未能实现我的个人目标,即找到我知道的特定会话 ID 的会话。因此,出于我的目的,我只是将 sessionid 添加为会话项(例如会话开始时的 Session["SessionId"] = session.SessionId。)然后我只是寻找具有匹配值的会话......我会更喜欢通过索引到其中一个集合来实际提取此条目,但这确实使它至少可以正常工作。

当然,这只是针对 In-Proc 会话,我确实正在考虑放弃。

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;
}

【讨论】:

    【解决方案5】:

    我一直在寻找与 @ajitdh 对更高版本 ASP.net 的答案的等效项 - 找不到任何东西,所以我想我会用 v4.6.2 的解决方案更新这个线程......还没有使用更高版本的 .net 进行了测试,但不适用于 v4.5。我猜它会兼容 v4.6.1 以后的版本。

    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!
                }
            }
        }
    }
    

    【讨论】:

    • 这应该是现在公认的答案。较旧的解决方案根本无法在较新版本的 .Net 中运行。
    【解决方案6】:

    这是一个 WebForms/Identity 示例,它获取最近 30 分钟内活跃的登录用户列表。

    以下答案适用于单个 Web 服务器,如果应用程序重新启动,缓存将丢失。如果您想保留数据并在网络场中的服务器之间共享数据,this answer 可能会很有趣。

    在 Global.asa.cs 中:

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

    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);
            }
        }
    }
    

    添加这几个类:

    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";
            }
        }
    }
    

    最后,在要显示活跃用户的页面中,显示结果:

    UserRepeater.DataSource = Global
        .ActiveUsersCache.GetActiveUsers().OrderByDescending(u => u.LastActive);
    UserRepeater.DataBind();
    

    【讨论】:

      【解决方案7】:

      据我所知,您无法使用标准的内存会话。这是我上周正在努力解决的问题,我决定除非您使用会话状态服务器,否则这是不可能的。如果你问我,从设计的角度来看似乎很奇怪。 :/

      【讨论】:

        【解决方案8】:

        你也可以把它当作基本的东西来尝试,这只是为了让你理解这个概念

        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)
        

        /* 将它添加到一个按钮,这样当它被点击时,它就会出现在 looged in 按钮*/ {

                    Label1.Text = "you are  Logged in "; 
        
        
            }
        

        【讨论】:

          【解决方案9】:

          试试下面的代码

           for (int i = 0; i < Session.Keys.Count - 1; i++)
                  {
                      Label1.Text += Session.Keys.Get(i) + " - " + Session[i].ToString()+"<br/>";
                  }
          

          【讨论】:

          • 这是对当前会话键的迭代。我想迭代并显示来自服务器会话状态中当前活动的 所有 会话的数据。
          猜你喜欢
          • 2012-02-09
          • 2017-12-10
          • 2018-01-24
          • 2021-07-20
          • 2023-03-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多