Introduction
The SessionState Attribute helps us to control the session state behavior in ASP.NET MVC. We can disable the session state / read-only / required for the controller using this attribute. This is a class-level attribute, so we can only apply this attribute at the controller level. Some of the action methods of a controller might have a different behavior than the controller session state behavior. In this case, the following solution is very useful. So, we can apply a session state behavior per action in ASP.NET MVC.
Problem Statement
We can control session state behavior using the SessionState attribute, but this can only be applied at the controller level. This means that all action method controllers have the same session state behavior. Now, if some of the action methods of the controller do not use a session and some of the action methods do use the session, then what is the solution?
Solution
In this scenario, we can create a different controller and move all the action methods that have the same session state behavior in the same controller class. This is not a good solution. Instead of doing this, we can create a custom action attribute that overwrites the behavior of the session state for the specific action method.
Use the following procedure to create a custom action attribute that overrides the behavior of the session state.
Step 1. Create a custom attribute.
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class ActionSessionStateAttribute : Attribute
{
public SessionStateBehavior Behavior { get; private set; }
public ActionSessionStateAttribute(SessionStateBehavior behavior)
{
this.Behavior = behavior;
}
}
Step 2. Create a custom controller factory.
public class CustomControllerFactory : DefaultControllerFactory
{
protected override SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
return SessionStateBehavior.Default;
}
var actionName = requestContext.RouteData.Values["action"].ToString();
MethodInfo actionMethodInfo;
actionMethodInfo = controllerType.GetMethod(actionName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
if (actionMethodInfo != null)
{
var actionSessionStateAttr = actionMethodInfo.GetCustomAttributes(typeof(ActionSessionStateAttribute), false)
.OfType<ActionSessionStateAttribute>()
.FirstOrDefault();
if (actionSessionStateAttr != null)
{
return actionSessionStateAttr.Behavior;
}
}
return base.GetControllerSessionBehavior(requestContext, controllerType);
}
}
Step 3. Register custom controller factory in Global. asax.
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(typeof(CustomControllerFactory));
}
Step 4. Attribute usage.
[SessionState(System.Web.SessionState.SessionStateBehavior.Disabled)]
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
TempData["test"] = "session less controller test";
return View();
}
[ActionSessionState(System.Web.SessionState.SessionStateBehavior.Required)]
public ActionResult About()
{
Session["test"] = "session less controller test";
return View();
}
}
The following is the output when the “Index” action method is called.

The following is the output when the “About” is called.

Conclusion
We can use the ActionSessionStateAttribute in combination with the controller-level attribute SessionStateAttribute. In this case, use the ActionSessionStateAttribute to overwrite the controller attribute on the actions to which it applies.

Jazz KhanPosted Sep 12, 2020, 1:04 AM
Doesn't work for readOnly property as i am still able to assign new value to Session["someVar"] variable in an action method decorated with readOnly property
Ed EaglehousePosted Nov 20, 2019, 11:59 AM
Thank you, Jignesh, for giving us a useful extension. Here's a little more information to help those of us who need this feature.The ActionSessionStateAttribute inplementation requires a reference to the library: System.Web.SessionState.The CustomControllerFactory implementation requires references to the libraries: System.Web.Mvc, System.Web.Routing, and System.Web.SessionState.By digging into the MVC source code, it turns out there are some public access points that we can leverage to find the correct action method. MVC internally already can map the correct route. By using the ControllerDescriptor.FindAction method, we can be confident that the action we find is the one that MVC will dispatch. Replace the controllerType.GetMethod() statement (that throws AmbiguousMatchException) with the following statements. It looks like a lot, but avoids writing our own logic to map route data to the action method. ControllerBase controller = null; MethodInfo actionMethodInfo = null; try { // Get a controller instance and use MVC routing to find the action method to invoke. controller = (ControllerBase)CreateController(requestContext, controllerName); var controllerContext = new ControllerContext(requestContext, controller); var controllerDescriptor = new ReflectedControllerDescriptor(controllerType); var actionDescriptor = (ReflectedActionDescriptor)controllerDescriptor.FindAction(controllerContext, actionName); actionMethodInfo = actionDescriptor.MethodInfo; } finally { // Release any resources the controller may have allocated. ReleaseController(controller); }
Mike SPosted Feb 24, 2019, 8:53 AM
Thank you for the post. When using your code I get this error: Ambiguous match found. near this code: actionMethodInfo = controllerType.GetMethod(actionName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
Jordan MunroePosted Nov 20, 2017, 8:56 AM
Also, this doesn't take into account the ActionNameAttribute. I wish there was a good way to acquire the action using the routing algorithm already extant in MVC itself.
Markus HamburgerPosted Dec 7, 2016, 3:58 AM
Simple solution, BUT WATCH OUT: Using reflection method "Type.GetMethod" assumes that every (!) controller in your project has unambiguous action names. As soon as you use, for example, "Index(int id)" and "Index(int id, int foo)" within a controller, CustomControllerFactory will throw an AmbiguousMatchException, even if the requested action doesn't use ActionSessionStateAttribute. I have changed reflection call to "GetMethods" and iterate through all matching actions. Nevertheless you must make your own decision, which action und attribute will fit best.
aadfeed ddeedssPosted Sep 15, 2016, 11:14 AM
Very slick, thank you!