Метод Session.SetString выдает исключение IFeatureCollection была удалена. Название объекта: «Коллекция». в ASP.NET Core 3.1

У меня есть проект, написанный на ASP.NET Core 3.1.

Мне нужно установить данные для сеанса в службе Singleton:

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

Я вызываю свой метод, как показано ниже:

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'. 

и я добавил код в метод ConfigureServices Startup.cs:

services.AddHttpContextAccessor();

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

И я добавил app.UseSession(); в метод Configure файла Startup.cs.

Я trid services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); и получаю сеанс от httpContextAccessor.HttpContext.Session, но получаю ту же ошибку.

Пожалуйста, помогите мне, спасибо.


person Ramil Aliyev    schedule 29.01.2020    source источник
comment
Откуда у вас это исключение?   -  person Llama    schedule 29.01.2020
comment
Это закрытая проблема в github. github.com/dotnet/aspnetcore/issues/14841   -  person Elvin Mammadov    schedule 29.01.2020
comment
@John в _session.SetString (SessionKeys.UserStatus, какое-то значение);   -  person Ramil Aliyev    schedule 29.01.2020
comment
Ссылка @RamilAliyev Elvin показывает, что проблема была вызвана неправильным использованием HttpContextAccessor. Вы все еще не опубликовали, где возникает ошибка. Где это _session.SetString(SessionKeys.UserStatus, "some value");? Откуда это _session, особенно в Singleton?   -  person Panagiotis Kanavos    schedule 29.01.2020
comment
HttpContext - это контекст одиночного запроса. Он обеспечивает доступ к запросу, свойствам ответа и т. Д. Этого единственного запроса. Вы не можете кэшировать его, он становится недействительным после завершения этого запроса.   -  person Panagiotis Kanavos    schedule 29.01.2020
comment
Почему вы используете сеанс в singleton? Сеансы для каждого пользователя, вам нужно знать, с каким пользователем / запросом вы работаете. Пожалуйста, разместите свой код. Где извлекается и кэшируется сеанс?   -  person Panagiotis Kanavos    schedule 29.01.2020
comment
@PanagiotisKanavos _session.SetString (SessionKeys.UserStatus, некоторое значение); находится в письменном методе одноэлементной службы, который я получаю от DI в конструкторе этой одноэлементной службы   -  person Ramil Aliyev    schedule 29.01.2020
comment
Тогда опубликуйте этот код! Вот что вызывает ошибку. Сеансы для каждого пользователя, к чьему сеансу пытается получить доступ синглтон? Чтобы получить это _session, вам пришлось использовать HttpContext временных запросов в качестве синглтона, что приводит к этой ошибке.   -  person Panagiotis Kanavos    schedule 29.01.2020
comment
Вы можете использовать службу Scoped вместо Singleton, которая заставит IHttpContextAccessor указывать на правильный контекст запроса.   -  person Panagiotis Kanavos    schedule 29.01.2020
comment
@PanagiotisKanavos Я уже редактировал.   -  person Ramil Aliyev    schedule 29.01.2020
comment
@RamilAliyev откуда это ISession? Ошибка очевидна - код пытается использовать просроченную сессию. Итак, вам нужно выяснить, откуда взялся этот просроченный сеанс. Если OperatorService является синглтоном, то гарантированно будет неправильный сеанс, как только второй пользователь зайдет на сайт. AddSession не регистрирует ISession в DI, откуда он взялся?   -  person Panagiotis Kanavos    schedule 29.01.2020
comment
@PanagiotisKanavos я добавил services.AddSession (options = ›{options.IdleTimeout = TimeSpan.FromMinutes (20); options.Cookie.HttpOnly = true;}) .AddDistributedMemoryCache (); в ConfigureServices и app.UseSession (); Настроить метод Startup.cs. Разве сеанс не отсюда?   -  person Ramil Aliyev    schedule 29.01.2020
comment
Нет, это не так. Вы добавили это где-то еще. AddSession даже не регистрирует ISession в службах внедрения зависимостей. Как вы создаете OperatorService? И почему это синглтон, а не сервис Scoped? Разместите код, который регистрирует или создает OperatorService, и код, который его использует. Вот что нужно исправить   -  person Panagiotis Kanavos    schedule 29.01.2020
comment
Хотя я уже ответил с возможными исправлениями - сделайте OperatorService сервисом Scoped, чтобы он мог получить правильный HttpContext, или сделайте ISession параметром ChangeOperatorStatus, чтобы вызывающие абоненты могли передавать правильный сеанс   -  person Panagiotis Kanavos    schedule 29.01.2020
comment
Я создаю OperatorService из DI в конструкторе контроллера. OperatorService реализован из IOperatorService и привязан к нему следующим образом: services.AddSingleton ‹IOperatorService, OperatorService› (); @PanagiotisKanavos   -  person Ramil Aliyev    schedule 29.01.2020
comment
Я не могу, чтобы служба была ограничена, потому что, поскольку она предоставляет другие службы во время работы проекта, поэтому она должна быть одноэлементной ((   -  person Ramil Aliyev    schedule 29.01.2020


Ответы (2)


HttpContext - это контекст одного запроса. Он обеспечивает доступ к запросу, свойствам ответа и т. Д. Этого единственного запроса. Вы не можете кэшировать его, он становится недействительным после завершения этого запроса.

Сессия - это еще одна временная вещь - она ​​существует только до тех пор, пока один пользовательский сеанс. Для каждого пользователя веб-приложения существует как минимум один сеанс. Кэширование одного из этих сеансов в синглтоне гарантирует, что

  • Ссылка станет недействительной через некоторое время, когда истечет сеанс и
  • Синглтон будет использовать только значения этого пользователя, игнорируя все остальные. Это ошибка сама по себе, и это отличный способ взломать приложение.
  • Если администратор входит в систему, объект сеанса может применять настройки администратора для всех в течение следующих 20, 30 или 60 минут.

Вот почему использование сеанса имеет смысл для промежуточного программного обеспечения для каждого запроса, а не для служб Singleton.

Правильное использование HttpContext

Сессия может быть достигнута только через контекст запроса, поэтому получение правильного сеанса означает получение правильного HttpContext. Правильный способ сделать это объясняется в Руководство Дэвида Фаулера по ASP.NET Core:

❌ ПЛОХО В этом примере 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");
        }
    }

✅ ХОРОШО В этом примере сам IHttpContextAccesor сохраняется в поле и используется поле HttpContext в правильное время (проверка на null).

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

Вместо этого используйте службу с ограниченной областью действия

Поскольку синглтон не может знать, какой сеанс использовать. Один из вариантов - просто преобразовать эту службу в службу с заданной областью действия. В ASP.NET Core запрос определяет область. Таким образом действия контроллера и промежуточное ПО конвейера получают доступ к правильному HttpContext для каждого запроса.

Предполагая, что служба используется действием или промежуточным программным обеспечением, возможно, единственное необходимое изменение - заменить AddSingleton<ThatService> на AddScoped<ThatService>.

Поворот столов или инверсия управления

Другой вариант: вызывающие этого синглтона должны предоставить ему сеанс. Вместо использования кэшированного сеанса, например:

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

Запросите сеанс или HttpContext в качестве параметра:

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

И пусть вызывающие абоненты передают ему правильный сеанс

person Panagiotis Kanavos    schedule 29.01.2020

Мне потребовалось время, чтобы это исправить. В моем случае это был aspnetcore 3.1, и он не работал, пока я не отключил функцию контейнера от

public async void OnPost

to

public async Task<IActionResult> OnPost

Похоже, HttpContext был удален до использования ...

person Sergisoft    schedule 05.09.2020
comment
Потому что вы не можете использовать ключевое слово await для метода async void. Поэтому вы должны использовать async Task вместо async void - person Ramil Aliyev; 17.01.2021