【问题标题】:ASP.NET 5 (Core): How to store objects in session-cache (ISession)?ASP.NET 5 (Core):如何在会话缓存 (ISession) 中存储对象?
【发布时间】:2016-06-29 20:07:04
【问题描述】:

我正在编写一个 ASP.NET 5 MVC 6 (Core) 应用程序。现在我需要在会话缓存 (ISession) 中存储(设置和获取)一个对象。

您可能知道,ISessionSet-方法采用 byte-arrayGet-方法返回一个。

在非核心应用程序中,我会使用BinaryFormatter 来转换我的对象。但是如何在核心应用程序中做到这一点?

【问题讨论】:

    标签: c# type-conversion bytearray asp.net-core session-state


    【解决方案1】:

    这是一个常青的帖子,问题仍然很新鲜,尽管 Microsoft 已建议序列化以将对象存储在会话中 - 除非您的对象是只读的,否则这不是正确的解决方案,我有一个博客解释所有场景 @987654321 @ 我什至在问题 id 18159 中指出了 Asp.Net Core GitHub 中的问题

    问题概要在这里:

    A.序列化与对象不同,它确实有助于分布式服务器场景,但它带有一个微软未能强调的警告 - 只有当对象意味着它时,它才会在没有任何不可预知的故障的情况下工作被读取而不是被写回。

    B.如果您在会话中寻找读写对象,则每次更改反序列化后从会话中读取的对象时 - 它需要通过调用序列化再次写回会话 - 仅此一项就可能导致多种复杂性因为您将需要跟踪更改 - 或在任何属性的每次更改后继续回写会话。在对服务器的一次请求中,您将遇到对象被多次写回直到返回响应的情况。

    C.对于会话中的读写对象,即使在单个服务器上它也会失败,因为用户的操作可以触发对服务器的多个快速请求,并且系统经常会发现自己处于对象正在被由一个线程序列化或反序列化并被另一个线程编辑然后写回,结果是您最终会被线程覆盖对象状态 - 甚至锁定也无济于事,因为对象不是真实对象而是反序列化创建的临时对象。

    D.序列化复杂对象存在问题 - 它不仅会影响性能,甚至在某些情况下可能会失败 - 特别是如果您有深度嵌套的对象,这些对象有时会引用自身。

    解决方案的概要在这里,完整的实现和代码在博客link

    1. 首先将其实现为 Cache 对象,在 IMemoryCache 中为每个唯一会话创建一项。

    2. 将缓存保持在滑动过期模式,这样每次读取它都会恢复过期时间 - 从而只要会话处于活动状态,对象就会一直保存在缓存中。

    3. 仅第二点是不够的,您将需要实施心跳技术 - 每 T 减去 1 分钟左右从 javascript 触发对会话的调用。 (我们过去常常这样做,甚至在用户使用浏览器之前保持会话处于活动状态,所以不会有任何不同

    其他建议

    A.创建一个名为 SessionManager 的对象 - 以便所有与会话读/写相关的代码都放在一个地方。

    B.不要为会话超时保持很高的值 - 如果您正在实施心跳技术,即使 3 分钟的会话超时也足够了。

    【讨论】:

    • 感谢详细的解释!所以信息是:“不要这样做,除非作为临时解决方案”?
    • 正确....就我而言,我必须这样做,因为我们正在过渡......在我们的长期计划中,我们将完全删除会话
    • @Kalpesh Popat - 你打算用什么来代替会话?
    • @dcp 目前我们正在存储用户凭据和会话中正在编辑的对象。计划在每个请求中获取用户凭据并提高查询效率并仅扫描该特定请求所需的权限,并使用缓存来加快速度。老实说,我还没有确定正在编辑的对象的解决方案——可能为每个对象创建一个 json 文件,在请求开始时读取它并在响应结束时写回。可以在文件上应用锁来解决并发问题。你有什么建议吗?
    • @Kalpesh Popat - 目前,我们正在使用 IDistributedCache 并将其注入到我们的控制器中。这仍然使用会话缓存,但它的工作方式与使用 HttpContext.Session 有所不同。 IDistributedCache 在数据库中的会话状态表中为每个键写出单独的行。如果您使用 HttpContext.Session,它会为 all 会话状态写出一行,正如您所指出的,这是一场并发噩梦。从长远来看,我们正在考虑使用 Azure Redis 缓存,但就目前而言,IDistributedCache 是一个足够好的解决方案。
    【解决方案2】:

    我会将对象序列化为 JSON,并使用ISession 上的扩展方法将它们保存为string

    // Save
    var key = "my-key";
    var str = JsonConvert.SerializeObject(obj);
    context.Session.SetString(key, str);
    
    // Retrieve
    var str = context.Session.GetString(key);
    var obj = JsonConvert.DeserializeObject<MyType>(str);
    

    ISession 上的扩展方法在 Microsoft.AspNet(Core).Http 命名空间中定义。

    【讨论】:

    • 感谢您的回答。为每个会话调用(可能几乎是每个请求)转换为 Json 是否高效?我应该把它当作一个干净的解决方案还是一个最好的解决方案?
    • 您可以阅读 dotnet github repo 上的this discussion about binary serialization,以及为什么不支持它。
    • @evaenrique 我的猜测是,它比对每个请求的二进制(反)序列化性能要高得多。您可以考虑使用protobuf.net,它是一个二进制序列化程序,速度非常快。
    • 感谢您让我走上正轨!您的意见很有帮助!
    • 我验证了您的“猜测”,即 json 转换比二进制序列化更高效。你完全正确。而对于大对象,json-(de)serialize 只是快一点,对于小对象,它只需要二进制-(de)serialize 的一半时间。
    猜你喜欢
    • 1970-01-01
    • 2015-09-25
    • 2010-12-16
    • 2017-08-25
    • 1970-01-01
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    相关资源
    最近更新 更多