Запись в переменную сеанса с помощью 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 никогда не блокирует сеанс
Ответ 2
создайте customControllerFactory, который наследует DefaultControllerFactory
и переопределяет GetControllerSessionBehavior()
. затем используйте ControllerBuilder.Current.SetControllerFactory()
для регистрации factory на Application_Start
см. http://www.c-sharpcorner.com/UploadFile/ff2f08/session-state-behavior-per-action-in-Asp-Net-mvc/
Ответ 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