In my ASP.NET MVC 5 application, I'm performing a GET
request on a method inside a controller that needs to read a value stored in session. To avoid session state locking issue, I've set SessionStateBehavior
to ReadOnly
on the class level.
[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)] public class TestController: Controller { var test = Session["KEY"]; ...
However, very occasionally, I need to overwrite the Session
variable to something else inside that same method. ASP.NET MVC does not allow me to do this with SessionStateBehavior
set to ReadOnly
. I can't set it to Required
because then I run into the issue of session state locking issue again, preventing concurrent AJAX requests.
What's a good solution for this?
Edit: We're using SQL server for session state management.
5 Answers
Answers 1
If you target .NET Framework 4.6.2 and you're on SQL Server, you could leverage the new async SessionState module to access the session storage providers asynchronously.
Download and install the SessionStateModule and the SqlSessionState NuGet packages.
Consider also that SQLServer mode stores session state in the SQL Server database.
Note (from the comments)
While the session in ASP.NET Core is non-locking, only next release of SqlSessionStateProviderAsync
should introduce this feature, according to this msdn blog.
Alternative provider
Another, different option would be to use StackExchange.Redis: e.g. for a web app in Azure App Service, follow these configuration steps. More generally, in a Redis server or servers, RedisSessionProvider never locks the Session
Answers 2
create a customControllerFactory which inherit with DefaultControllerFactory
, and override the GetControllerSessionBehavior()
. then use ControllerBuilder.Current.SetControllerFactory()
to register the factory at Application_Start
see http://www.c-sharpcorner.com/UploadFile/ff2f08/session-state-behavior-per-action-in-Asp-Net-mvc/
Answers 3
Because you're using a SQL Server session backend, I think you can achieve what you want by manually interacting with the session database and bending it to your will.
Familiarize yourself with the workings of a session provider, in particular the SqlSessionStateStore
class. Under the hood, all it's doing is (de)serializing your data and calling the stored procedures in the session state database (specifically, check out SetAndReleaseItemExclusive
).
If you can figure out how to construct a SessionStateStoreData
object, or more specifically the ISessionStateItemCollection
needed to construct it, from the session collection you have access to via the HttpContext
plus whatever changes you want to make, you can then use reflection to call the internal static SessionStateUtility.SerializeStoreData
method. After that, it should be trivial to call the correct stored procedure (based on the updated session's size).
Answers 4
You can dynamically set the session state behavior for every request. Based on some condition you can switch to SessionStateBehavior.Required
and in any other case just use the default behavior.
You will need a custom MvcRouteHandler:
public class MyMvcRouteHandler : MvcRouteHandler { protected override SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext) { if (someCondition) { return SessionStateBehavior.Required; } // fallback to default behavior return base.GetSessionStateBehavior(requestContext); } }
In the requestContext
you will find HttpContext
and based on that you can decide what to do.
You will need to set this new RouteHandler
for every necessary route. You can do this in the RouteConfig
file, put this after route registrations:
// ... 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(); }
Answers 5
The issue is a logic issue and two opposite business requirements which are: the readOnly and the need to modify that given some conditions. So, what i would do is to go back to your logic and cover the conditions. I would use the SessionStateBehavior() to sort of toggle. For example, add a simple if statement as follows (sudo code)
If a condition is met that requires non readOnly then SetSessionStateBehavior( SessionStateBehavior required )
// here do what you need to do then set it back
0 comments:
Post a Comment