【问题标题】:Storing different datatypes in the asp.net session在 asp.net 会话中存储不同的数据类型
【发布时间】:2017-12-21 04:27:17
【问题描述】:

与同事聊天后,我很想知道是否有人进行过任何研究/测试,以了解在 asp.net 会话中存储数据的最有效和推荐的方法。

我最近偶然发现了这个问题:How to access session variables from any class in ASP.NET? 并开始使用该技术(创建我自己的会话类以赋予我智能感知的优势并防止在从会话中检索数据时需要显式转换)。

但是,有人建议将我的对象中的数据序列化为字符串,然后存储...然后在检索时反序列化,这可能不是很有效。

例如...如果我想加载用户当前的权限并将它们存储在会话中;我的会话对象中可能有一个属性定义为List<int> user_permissions,然后在每个页面上我都可以if (session_class.user_permissions.contains(7))

或者,我可以直接将会话存储为字符串,例如; HttpContext.Current.Session["user_permissions"] = "[1][4][8][12]" 然后在每个页面上我都可以查看HttpContext.Current.Session["user_permissions"].contains("[7]")

哪个选项更有效并且可以量化?

【问题讨论】:

  • 与其使用字符串来有效地表示按位值,为什么不使用基于标志的枚举并存储整数值呢?更高效,您可以使用枚举值而不是像 "[7]" 这样的字符串,这没有任何意义。
  • 我刚刚看了看,这看起来是一个非常有趣的解决方案,但我想知道如果我们拥有大量权限,它的效果如何?
  • 那么你想了多少?您可以拥有一个基础类型为long 的枚举,因此您将获得 64 位...
  • 我认为这不会削减它。我可能已经想到了大约 50 个,并且我们希望确保我们的应用程序是可扩展的,因此从一开始就对其进行设计似乎是一种糟糕的方式。不过好主意-谢谢
  • 所以有两个枚举和两个值。这将涵盖 128 个。如果有 128 个独立权限,我认为您将遇到 people 了解您的系统的扩展问题。

标签: c# asp.net session session-variables


【解决方案1】:

假设您只在一台机器上托管(因此会话状态不需要序列化到公共存储库),您可以使用公共 MemoryCache 将会话数据托管在内存中,由唯一的会话 id 键控.

public class SessionData
{
    private static readonly MemoryCache Cache = new MemoryCache("SessionData");
    private static readonly CacheItemPolicy Policy = new CacheItemPolicy {SlidingExpiration = new TimeSpan(0, 30, 0)};

    public static SessionData Get(string sessionId)
    {
        if (Cache.Contains(sessionId))
            return Cache[sessionId] as SessionData;

        var item = new SessionData {SessionId = sessionId};
        Cache.Add(sessionId, item, Policy);
        return item;
    }

    public string SessionId { get; set; }
    public string Foo { get; set; }
    public int Bar { get; set; }
}

使用示例:

var item = SessionData.Get(this.Session.SessionID);
item.Foo = "Hello";
item.Bar = 123;

会话数据以 30 分钟的滑动到期时间保存在内存中 - 每次访问时,都会重置 30 分钟的超时时间。这样您就不会因会话过期而导致内存膨胀。试试你当前的实现,看看它是如何叠加的。

【讨论】:

  • 非常感兴趣,我已经看到 system.web.caching.cache 提到过几次了。所以你能确认......存储在会话中的数据在设置时被序列化,在获取时被反序列化?这是低效的吗?但是存储在 system.web.caching.cache 中的数据不是,因此性能更高?
  • MemoryCache 肯定不会序列化数据——它不需要,因为数据总是驻留在内存中,直到它过期并从缓存或缓存中释放的阶段才被垃圾收集本身就是垃圾收集。然而,这种方法的一个潜在问题是 IIS 应用程序池每 29 小时回收一次 - 我感觉静态对象不会被持久化,因此使用内置会话对象可能会更好。跨度>
  • 谢谢皮特,我现在相信在使用 inproc 模式时会话没有序列化,那么 MemoryCache 有什么优势呢? (抱歉,我可能遗漏了一些明显的东西)
  • 嗨 Lee - 虽然 MemoryCache 是高性能的,并且会自动整理不再需要/过期的会话数据,但它可能不比内置会话对象性能更高 - 我只是将它提供为我知道的替代方案不会序列化。根据理解,当在 InProc 模式下使用会话时,我建议坚持使用它,因为当应用程序池回收时,MemoryCache 的内容可能会丢失。
【解决方案2】:

首先要确定 ASP.NET 能够开箱即用地序列化哪些内容。由于 ASP.NET 参考源可用,我们可以将状态序列化下钻到此类:AltSerialization

显示原生支持以下类型:

String
Int32
Boolean
DateTime
Decimal
Byte
Char
Single
Double
SByte
Int16
Int64
UInt16
UInt32
UInt64
TimeSpan
Guid
IntPtr
UIntPtr
Null // not really a type :-)

对于所有其他类型,系统使用BinaryFormatter,这是一个公共 .NET 类。 BinaryFormatter 非常易于使用,默认情况下它知道许多类,因此您可以进行简单的基准测试,比较 BinaryFormatter 将生成的内容与您自己“手动”生成的内容。

一般而言,您总是能够超越 BinaryFormatter 的性能,因为您更了解自己的数据,但这意味着您必须编写额外的序列化/反序列化代码。您还可以创建自定义类并实现ISerializable

最后一点,如果您查看 AltSerialization 源代码,您会看到:

internal static void WriteValueToStream(Object value, BinaryWriter writer)
{
    if (known type) { ... }
    else
    {
        ...
        var formatter = new BinaryFormatter();
        if (SessionStateUtility.SerializationSurrogateSelector != null)
        {
            formatter.SurrogateSelector = SessionStateUtility.SerializationSurrogateSelector;
        }

        formatter.Serialize(writer.BaseStream, value);
    }
}

这意味着您可以实现一个全局挂钩以在一个中心位置处理特定类型,这在工程或进行基准测试方面非常有用(您可以轻松启用或禁用它)。它可能比创建可序列化的自定义类更好。

【讨论】:

    【解决方案3】:

    我在您的帖子中发现了两个问题。

    1.在 asp.net 会话中存储不同的数据类型

    你可能知道,在 ASP.NET 中保持 Session 的方式有以下三种:InProcStateServerSqlServer

    InProc 模式下,您可以为会话的值设置任何对象而无需序列化,因为会话和网络运行在同一个进程中。

    但是对于其他方式,当值不是结构时,会话的值必须被序列化。 SateServerSqlServer 在独立进程上运行(可能在独立服务器上),所以会话的值将被序列化并在您设置时保留在另一个进程上。

    2。哪个选项更有效,是否可以在List.contains()String.contains() 中量化?

    如果您的情况下没有大数据,它们并没有太大区别。每个都有使用循环。如果你想在contains获得良好的效率,你可以试试HashSet

    【讨论】:

      【解决方案4】:

      问题实际上是您的方法的性能有多重要,而您拥有可维护和可读的代码库有多重要?

      您使用哪个框架来序列化和反序列化数据也会有所不同,可能值得对其进行一些基本的速度测试。如果你能在一秒钟内处理千万亿次请求,那可能没问题。

      您的问题明确提出“哪个选项会更有效并且可以量化?”是的。绝对地。只需启动StopWatch(请参阅System.Diagnostics 我相信是)并使用一些测试数据多次调用该方法,然后检查秒表的时间。

      附言您的问题表明了关于这个 Session 类需要如何性能的讨论......根据我的经验,在网络应用程序中,(反)序列化并不是主要关注的问题。是数据库瓶颈和其他长时间运行的进程减慢了服务器速度。如果这个问题是因为“服务器已经很慢,所以这只是额外的......”的论点而被问到,然后告诉他们解决其他问题,这就像在 2成吨的砖块真的做到了。只是预感这就是论点:P

      【讨论】:

      • 嗨,Dan,该应用程序目前的性能实际上令人难以置信(我们只进行了几个月的构建),但我们希望尽可能在知情的情况下做出每一个决定。当然,如果我们需要扩展到多台服务器并将会话移出进程之外,当然还有考虑......
      【解决方案5】:

      我认为我们已经找到了一个合理的理由来确定存储对象(尤其是使用自定义类包装技术How to access session variables from any class in ASP.NET?)不是最好的方法。据我们了解,这会将类的所有属性(字段)存储在内存中,即使是空的也是如此。

      public class my_session {
       public int iUserID;
       public string sUserName;
       public List<int> lsUSerPermissions;
       public List<TestStatus> lsTestStatuses;
       public int iSomethingElse;
       public bool bSomething;
      }
      

      尽管我们不会为所有会话填充/设置所有属性,但每个会话都将包含所有属性(并消耗相关的内存)。最初我虽然这很奇怪,但现在我明白这是有道理的,所以如果/当你设置每个属性时,内存是可用的,并且对象不必被销毁并在其他地方重新创建。

      如果我们直接赋值,我们只需要为那些需要存储的属性创建会话变量。

      所以,既然我们决定单独存储它们;

      Session["iUserID"] = 1;
      Session["sUserName"] = "Lee";
      

      我们如何处理我们“必须”存储的对象? 似乎每个对象都需要单独查看。 string sUserPermissions = "[ViewUser][CreateUser][EditUser]" 占用的内存比 List&lt;string&gt; lsUserPermissions = new List&lt;string&gt;() { "[ViewUser]", "[CreateUser]", "[EditUser]" } 少吗?如果需要,序列化/反序列化是否更快?

      仍然需要大量的研究。

      【讨论】:

        猜你喜欢
        • 2021-10-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-06-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多