Запись в переменную сеанса с помощью SessionStateBehavior.ReadOnly

В моем приложении ASP.NET MVC 5 я выполняю запрос GET по методу внутри контроллера, которому необходимо прочитать значение, хранящееся в сеансе. Чтобы избежать проблемы блокировки состояния сеанса, я установил SessionStateBehavior в ReadOnly на уровне класса.

 [SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
    public class TestController: Controller
    {
         var test = Session["KEY"];
...

Однако очень редко мне приходится перезаписывать переменную Session в нечто другое внутри этого же метода. ASP.NET MVC не позволяет мне делать это с SessionStateBehavior, установленным на ReadOnly. Я не могу установить его на Required, потому что тогда я снова столкнулся с проблемой блокировки состояния сеанса, предотвращая одновременные запросы AJAX.

Какое хорошее решение для этого?

Изменить: мы используем SQL-сервер для управления состоянием сеанса.

Ответ 1

Если вы нацелились на .NET Framework 4.6.2, и вы находитесь на SQL Server, вы можете использовать новый модуль async SessionState для доступа провайдеры хранения сеанса асинхронно.

Загрузите и установите SessionStateModule и SqlSessionState пакеты NuGet.

Считайте также, что режим SQLServer сохраняет состояние сеанса в базе данных SQL Server.

Примечание (из комментариев)

В то время как сеанс в ядре ASP.NET не является блокировкой, только следующая версия SqlSessionStateProviderAsync должна ввести эту функцию в соответствии с этим msdn blog.

Альтернативный поставщик

Другим, другим вариантом будет использование StackExchange.Redis: например. для веб-приложения в Azure App Service выполните следующие шаги настройки. В общем случае на сервере или серверах Redis RedisSessionProvider никогда не блокирует сеанс

Ответ 3

Поскольку вы используете серверную сессию SQL Server, я думаю, вы можете добиться того, чего хотите, вручную взаимодействуя с базой данных сеанса и сгибая ее по своему желанию.

Ознакомьтесь с работой поставщика сеанса, в частности SqlSessionStateStore. Под капотом все, что он делает, - это (де) сериализация ваших данных и вызов хранимых процедур в базе данных состояния сеанса (в частности, проверка SetAndReleaseItemExclusive).

Если вы можете выяснить, как создать объект SessionStateStoreData, а точнее ISessionStateItemCollection, необходимых для его создания, из коллекции сеансов, к которой вы имеете доступ, через HttpContext плюс любые изменения, которые вы хотите сделать, затем вы можете использовать отражение для вызова внутреннего статического SessionStateUtility.SerializeStoreData. После этого должно быть тривиально вызывать правильную хранимую процедуру (на основе обновленного размера сеанса).

Ответ 4

Вы можете динамически устанавливать поведение состояния сеанса для каждого запроса. Исходя из некоторого условия, вы можете переключиться на SessionStateBehavior.Required, и в любом другом случае просто используйте поведение по умолчанию.

Вам понадобится пользовательский MvcRouteHandler:

public class MyMvcRouteHandler : MvcRouteHandler
{
    protected override SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext)
    {
        if (someCondition)
        {
            return SessionStateBehavior.Required;
        }
        // fallback to default behavior
        return base.GetSessionStateBehavior(requestContext);
    }
}

В requestContext вы найдете HttpContext и на основе этого вы можете решить, что делать.

Вам нужно будет установить новый RouteHandler для каждого необходимого маршрута. Вы можете сделать это в файле RouteConfig, поместите это после регистрации маршрута:

// ...
routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
// ...

// set MyMvcRouteHandler for every route
foreach (var route in routes.OfType<Route>())
{
    route.RouteHandler = new MyMvcRouteHandler();
}

Ответ 5

Проблема - это логическая проблема и два противоположных бизнес-требования, которые: readOnly и необходимость изменения, учитывая некоторые условия. Итак, что бы я сделал, это вернуться к вашей логике и покрыть условия. Я бы использовал метод Установить SetSessionStateBehavior() для сортировки переключения. Например, добавьте простой оператор if (sudo code)

    If a condition is met that requires non readOnly then
     SetSessionStateBehavior(
    SessionStateBehavior required
 )

//сделаем то, что вам нужно, затем установите его

Ответ 6

Вы можете попробовать версию Microsoft.AspNet.SessionState.SqlSessionStateProviderAsync 1.1.0, которая поддерживает одновременные запросы с тем же sessionid. Вот шаги для использования этого nupkg. (вы можете найти более подробную информацию о пакете на этот блог)

  • установить SqlSessionStateProviderAsync 1.1.0
  • добавить appsetting в web.config