【问题标题】:Is there a way to use session for objects in asp.net core like in web forms?有没有办法像 web 表单一样在 asp.net 核心中为对象使用会话?
【发布时间】:2019-06-23 13:04:26
【问题描述】:

我有一个 Web 表单项目,我正在尝试将其转换为 asp.net 核心。我的问题是,在网络表单中,我将一个对象保存到会话中,当我得到它时,我可以对其进行更改并保留它们。另一方面,在核心中,我通过序列化将对象设置为会话。当我得到它并进行更改时,它不会影响存储在会话中的对象。有没有办法按原样保存对象(不序列化它),所以当我从会话中获取它并对其进行更改时,继续使用它并且不必再次在会话中设置它?

会议是

 System.Web.HttpContext.Current.Session["object"]

成功了

AppHttpContext.Current.Session.GetString("object") 

然后我将它反序列化为我想要的对象。

Ps:我使用 AppHttpContext 在控制器之外获取会话

【问题讨论】:

  • 这是一个 X/Y 问题。你可以让会话工作,但你应该尽量避免这种情况。你剩下的问题不是很清楚,什么是“没有序列化”?
  • 某处的静态MemoryCache 怎么样。然后使用键SessionId 和值Dictionary<string, object> 作为会话条目的存储。

标签: c# session asp.net-core httpcontext


【解决方案1】:

迁移任务不会一帆风顺。

您可以按照以下步骤操作:

  1. 首先将您对 Session 的依赖替换为可以注入控制器或其他服务的某种服务。一个好主意是在此服务中硬编码值,以检查是否所有代码都取决于您之前的会话实现能够使用新服务。

  2. 实现一个过滤器(例如通过在 Razor 页面的情况下实现 IAsyncPageFilter)来接入 HttpContext 用户并设置某种可以从缓存实现中检索的查找对象 - 一个好主意是使用分布式的 Redis缓存。这个对象应该有 Key 作为用户身份 - 值可以是您可能想要添加到“会话”的任何数据。使用“缓存”服务来实现这一点,从客户端代码中隐藏缓存实现细节。 (请记住创建这些“请求”范围依赖项,以便适当地填充 HttpContext 用户。)

  3. 在新服务上实现方法以添加/修改缓存对象以保持您的“会话”数据更新。

故事的寓意:减少对平台特定功能的依赖。至少使用某种服务封装这些依赖项,这些服务可以在以后根据这些特性在没有客户端代码的情况下替换。

【讨论】:

    【解决方案2】:

    ASP.NET 核心不支持将对象直接保存到 Session 中,而 Web 窗体项目可以这样做。在 ASP.NET Core 中,仅直接支持将原始类型保存到 Session 中,例如 stringintbyte 数组。所以任何更复杂的对象都需要序列化为字符串或直接序列化为字节数组,然后可以将序列化存储在Session中。

    人们通常通过对对象进行 JSON 序列化以生成字符串,然后将字符串保存到 Session 来完成此操作。但是,当您从 Session 检索 JSON 字符串并将其反序列化回对象时,对该对象的任何更改都不会反映在表示当前存储在会话中的对象的先前 JSON 中。所以对象的变化不会直接反映在Session中。

    这与在 Web 窗体中看到的行为非常不同,这是由于对象没有存储在会话中,而是序列化。因此,如果您更改对象并希望将这些更改存储在会话中,则需要再次序列化对象并将该序列化(例如 json)存储在会话中。

    这有帮助吗?

    【讨论】:

    • 是的,但是完成上述场景的替代方法是什么?
    • 如果你想使用 ASP.NET Core 提供的 Session 工具,这是无可替代的。我想您可以说另一种方法是编写自定义会话支持。例如,我这样做是为了能够将我的会话存储在 Sql Server 中,并在会话密钥级别提供乐观锁定支持,但我认为大多数使用 ASP.NET Core 的人都不会编写自己的自定义会话支持。 :-)
    • longdatetime 原始类型怎么样。看起来除了字符串、整数和字节之外的任何东西,你必须序列化和存储
    • @LP13 .NET 生态系统中可能有第 3 方扩展方法,但 Microsoft 提供的用于 Session 对象的扩展方法不包括对 long 或 datetime 的支持,请参阅 @987654321 @如果有帮助,别忘了点赞我的回答。
    • 明白。但不确定为什么微软决定忽略其他原始类型。通常,您将要在每个请求上读取/写入的内容放入会话中。为每个请求序列化/反序列化原始类型是不必要的开销
    【解决方案3】:

    要在会话中使用对象/数组,您需要进行一些自定义:`

    public static class SessionExtensions {
    public static void SetObject(this ISession session, string key, object value)
    {
        session.SetString(key, JsonConvert.SerializeObject(value));
    }
    
    public static T GetObject<T>(this ISession session, string key)
    {
        var value = session.GetString(key);
        return value == null ? default(T) : JsonConvert.DeserializeObject<T>(value);
    } }
    

    现在你可以这样设置:

    var objComplex = new ComplexClass();
    HttpContext.Session.SetObject("ComplexObject", objNew);
    

    然后检索它:

    var objComplex = HttpContext.Session.GetObject("ComplexObject");
    

    【讨论】:

    • 这适用于很多不同的场景,但它不是存储对象。您将字符串存储为序列化对象。有什么区别?您无权访问对象元数据信息,例如,如果您使用多态性,它将无法正常工作。
    【解决方案4】:

    从技术上讲,ASP.NET 也从不支持这一点。由于会话存储在这种情况下的工作方式,在使用 InProc(即在内存中)会话时,这是一个侥幸。如果您使用 SQL Server 之类的备用会话存储,则还必须在会话内外序列化/反序列化对象。 ASP.NET Core 的不同之处仅在于它使用通用会话存储,因此它强制对任何对象进行序列化/反序列化,即使您使用的是内存存储。

    也就是说,您的问题是您根本没有将更改保留回来。同样,您依赖于意外行为。因为 ASP.NET 直接将对象持久保存在内存中,所以对该对象执行的操作会自动“持久化”,因为它是同一个引用。反序列化时,您正在创建一个新实例,因此除非您随后将对象序列化回会话中,否则操作将不会持续存在。

    最后,不管是不是 Web 表单,这从来没有推荐,甚至是正确的做事方式。会话存储是易失的。除了短期(和不可靠)的持久性之外,它不用于任何其他用途。换句话说,您永远不能指望会话中存在某些内容,因此您应该始终围绕数据不存在的情况进行防御性编码。如果你依赖于存在的东西,就像这里听起来一样,那么你总是在为失败做准备。

    但是,如果您正在执行多步骤表单之类的操作,则可以接受各个步骤进入会话,尽管您应该实际上使用TempData 而不是@987654322 @ 为了那个原因。但是,在最后一步,您的组合对象应该保存在 SQL Server 之类的东西中,而不是会话中。在几乎所有其他情况下,您应该简单地坚持使用 SQL Server 之类的东西,并完全避免使用 Session

    【讨论】:

      猜你喜欢
      • 2017-05-27
      • 1970-01-01
      • 1970-01-01
      • 2010-10-21
      • 2013-02-11
      • 2016-03-23
      • 1970-01-01
      • 2011-06-10
      • 1970-01-01
      相关资源
      最近更新 更多