【问题标题】:Session.SetString method throws exception "IFeatureCollection has been disposed. Object name: 'Collection'. " in ASP.NET Core 3.1Session.SetString 方法在 ASP.NET Core 3.1 中引发异常“IFeatureCollection 已被释放。对象名称:'Collection'。”
【发布时间】:2020-01-29 08:47:36
【问题描述】:

我有一个用 ASP.NET Core 3.1 编写的项目。

我需要在 Singleton 服务中给 Session 设置数据:

 _session.SetString("some key", "some value");

我从 DI 注入了会话对象:

public OperatorService(ILogger<OperatorService> logger, 
                       ISession session,
                       IOptions<AppSettings> options)
{
     this._session = session; 
     this._logger = logger; 
     this._appSettings = options.Value;
}

我调用 my 方法如下:

public void ChangeOperatorStatus(StatusChangeRequest request)
{
     try
     {
         _session.SetString(request.Key, request.Value);
     }
     catch (Exception ex)
     {
          _logger.LogInformation($"Exception while changing status: {ex}"); 
     } 
} 

但我得到以下异常:

IFeatureCollection has been disposed.\r\nObject name: 'Collection'. 

我在 Startup.cs 的 ConfigureServices 方法中添加了一些代码:

services.AddHttpContextAccessor();

services.AddSession(options =>
{
    options.IdleTimeout = TimeSpan.FromMinutes(20);
    options.Cookie.HttpOnly = true;
})
.AddDistributedMemoryCache();

我在 Startup.cs 的 Configure 方法中添加了app.UseSession();

我尝试了services.AddSingleton&lt;IHttpContextAccessor, HttpContextAccessor&gt;(); 并从httpContextAccessor.HttpContext.Session 获得了会话,但我得到了同样的错误。

请帮帮我,谢谢。

【问题讨论】:

  • 你从哪里得到这个异常?
  • 这是 github 中的一个已关闭问题。 github.com/dotnet/aspnetcore/issues/14841
  • @John in _session.SetString(SessionKeys.UserStatus, "some value");
  • @RamilAliyev Elvin 的链接显示问题是由错误使用 HttpContextAccessor 引起的。您仍然没有发布错误发生的位置。 _session.SetString(SessionKeys.UserStatus, "some value"); 是哪里? _session 是从哪里来的,尤其是在 Singleton 中?
  • 一个 HttpContext 是一个 single 请求的上下文。它提供对单个请求的请求、响应属性等的访问。你不能缓存它,一旦请求结束它就会失效。

标签: c# .net asp.net-core asp.net-core-3.1


【解决方案1】:

HttpContext 是单个请求的上下文。它提供对单个请求的请求、响应属性等的访问。你不能缓存它,一旦请求结束它就会失效。

会话是另一个短暂的东西——它的存在时间与单个用户会话一样长。 Web 应用程序的每个用户至少有一个会话。在单例中缓存其中一个会话可以保证

  • 一段时间后,当会话到期时引用将失效,并且
  • 单例将只使用该用户的值,而忽略其他所有人的值。这本身就是一个错误,也是入侵应用程序的好方法
  • 如果管理员登录,Session 对象可能会在接下来的 20、30 或 60 分钟内将管理员的设置实时应用到所有人。

这就是为什么使用 Session 对每个请求的中间件有意义,而不是 Singleton 服务。

HttpContext的正确使用

Session只能通过请求的上下文来到达,所以获取正确的会话就意味着获取了正确的HttpContext。 David Fowler's ASP.NET Core Guidance 中解释了正确的方法:

❌ BAD 此示例将 HttpContext 存储在一个字段中,然后尝试使用它。

    private readonly HttpContext _context;
    public MyType(IHttpContextAccessor accessor)
    {
        _context = accessor.HttpContext;
    }
    public void CheckAdmin()
    {
        if (!_context.User.IsInRole("admin"))
        {
            throw new UnauthorizedAccessException("The current user isn't an admin");
        }
    }

✅ GOOD 此示例将 IHttpContextAccesor 本身存储在一个字段中,并在正确的时间使用 HttpContext 字段(检查是否为空)。

   private readonly IHttpContextAccessor _accessor;
   public MyType(IHttpContextAccessor accessor)
   {
       _accessor = accessor;
   }

   public void CheckAdmin()
   {
       var context = _accessor.HttpContext;
       if (context != null && !context.User.IsInRole("admin"))
       {
           throw new UnauthorizedAccessException("The current user isn't an admin");
       }
   }

改用 Scoped 服务

因为 Singleton 不知道要使用哪个会话。一种选择是简单地将该服务转换为 Scoped 服务。在 ASP.NET Core 中,请求定义了一个范围。这就是控制器操作和管道中间件如何为每个请求访问正确的 HttpContext。

假设服务由操作或中间件使用,也许唯一需要的更改是将AddSingleton&lt;ThatService&gt; 替换为AddScoped&lt;ThatService&gt;

扭转局面,或控制反转

另一个选项是 callers 的单身人士应该向它提供会话。而不是使用缓存会话,例如:

public void SetStatus(string status)
{
    _session.SetString(SessionKeys.UserStatus, "some value");
}

要求 session 或 HttpContext 作为参数:

public void SetStatus(string status,ISession session)
{
    session.SetString(SessionKeys.UserStatus, "some value");
}

并让调用者将正确的会话传递给它

【讨论】:

    【解决方案2】:

    我花了一段时间才解决这个问题。 就我而言,它是 3.1 aspnetcore,直到我从

    public async void OnPost

    public async Task&lt;IActionResult&gt; OnPost

    看起来 HttpContext 在使用之前就被释放了...

    【讨论】:

    • 因为您不能将 await 关键字用于 async void 方法。因此,您必须使用 async Task 而不是 async void
    猜你喜欢
    • 1970-01-01
    • 2021-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-06
    • 2020-01-02
    相关资源
    最近更新 更多