Session State on Action

In my previous post I have mentioned that Session State is only at controller level. But if someone what at both level  i.e controller and action like at controller level session state behavior is “Read-Only” and at action level we want “Default”. Now what?  By end of this post you will learn how to achieve this.

We will be using “OVERIDDING” feature to implement this.

Steps:

  1.  Create custom Attribute
  2.  Override the “GetControllerSessionBehavior” method present in class DefaultControllerFactory.
  3.  Register it in

1. Creating custom Attribute

public sealed class ActionSessionStateAttribute : Attribute
{
        public SessionStateBehavior SessionBehavior { get; private set; }          
        public ActionSessionStateAttribute(SessionStateBehavior sessionBehavior)
        {
            SessionBehavior = sessioBehavior;
        }
}

2. Overriding of method

public class SessionControllerFactory : DefaultControllerFactory
{       
        protected override SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType)
        {
            if (controllerType == null)
                return SessionStateBehavior.Default;
           
            var actionName = requestContext.RouteData.Values["action"].ToString();
            Type typeOfRequest=requestContext.HttpContext.Request.RequestType.ToLower() =="get"?typeof(HttpGetAttribute):typeof(HttpPostAttribute);
            // [Line1]
            var cntMethods = controllerType.GetMethods()
                   .Where(m => 
                    m.Name == actionName &&
                    (  (  typeOfRequest == typeof(HttpPostAttribute) && 
                          m.CustomAttributes.Where(a => a.AttributeType == typeOfRequest).Count()>0
                       )
                       ||
                       (  typeOfRequest == typeof(HttpGetAttribute) &&
                          m.CustomAttributes.Where(a => a.AttributeType == typeof(HttpPostAttribute)).Count() == 0
                       )
                    )
                );
            MethodInfo actionMethodInfo = actionMethodInfo = cntMethods != null && cntMethods.Count() == 1 ? cntMethods.ElementAt(0):null;
            if (actionMethodInfo != null)
            {
                var sessionStateAttr = actionMethodInfo.GetCustomAttributes(typeof(ActionSessionStateAttribute), false)
                                    .OfType<ActionSessionStateAttribute>()
                                    .FirstOrDefault();

                if (sessionStateAttr != null)
                {
                    return sessionStateAttr.Behavior;
                }
            }
            return base.GetControllerSessionBehavior(requestContext, controllerType);
 }

3. Register class in Global.asax

public class MvcApplication : System.Web.HttpApplication
 {
        protected void Application_Start()
        {
            // --- other code ---
            ControllerBuilder.Current.SetControllerFactory(typeof(SessionControllerFactory));
        }
}

4. Example :

[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
public class TestController : CustomController
{
       [ActionSessionState(SessionStateBehavior.Default)]
        public ActionResult TestSession()
        {
            HttpContext.Session["test"]="India";
            return View();
        }
}

Common Mistakes and Code Explanation

Some people uses below code in place of line 1

MethodInfo actionMethodInfo = controllerType.GetMethod(actionName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);

But this will give you “System.Reflection.AmbiguousMatchException” exception. As many controller has two action with same name but one with HttpGet and other with HttpPost. Because of this when GetMethod is called it find two method with same that’s why it throws the exception.

To resolve this issue  use GetMethods and LINQ.

Note: In controller writing [HttpGet] on action is not compulsory for get methods i.e. on action if HttpGet is not written then by default .net will consider it as [HttpGet]. But for post action it is compulsory. Will use this property for finding the correct method.

Steps to 

1. Find Type of request

var actionName =requestContext.RouteData.Values["action"].ToString();
Type typeOfRequest = filterContext.HttpContext.Request.RequestType.ToLower() =="get"?typeof(HttpGetAttribute):typeof(HttpPostAttribute);

2. Find the action using reflection

var cntMethods = controllerType.GetMethods()
                 .Where(m => 
                    m.Name == actionName &&
                    (  (  typeOfRequest == typeof(HttpPostAttribute) && 
                          m.CustomAttributes.Where(a => a.AttributeType == typeOfRequest).Count()>0
                       )
                       ||
                       (  typeOfRequest == typeof(HttpGetAttribute) &&
                          m.CustomAttributes.Where(a => a.AttributeType == typeof(HttpPostAttribute)).Count() == 0
                       )
                    )
                );
            MethodInfo actionMethodInfo = actionMethodInfo = cntMethods != null && cntMethods.Count() == 1 ? cntMethods.ElementAt(0):null;

 

Session State Module Example

This post will tell  you the example of Default, Disabled and Read-Only session module

Session State :  Default

Controller :

  [SessionState(System.Web.SessionState.SessionStateBehavior.Default)]
   public class TestController
   {
        public ActionResult TestSession()
        {
            HttpContext.Session["TestSession"] = "India";
            return View();
        }

   }

View :

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Test Session</title>
</head>
<body>
         Session value "@Session["TestSession"].ToString()"
</body>
</html>

 

Session State :  Disable

Controller :

  [SessionState(System.Web.SessionState.SessionStateBehavior.Disable)]
   public class TestController
   {
        public ActionResult TestSessionAction()
        {
            HttpContext.Session["TestSession"] = "India";
            return View();
        }

   }

As in disable you cannot neither read nor write session. This code will throw the “Null Reference” exception.

Session State :  Read-Only

Suppose initial value HttpContext.Session[“TestSession”] is “India”.

Controller :

  [SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
   public class TestController
   {
        public ActionResult TestUpdateSession()
        {
            HttpContext.Session["TestSession"] = "Pune";
            return View();
        }
        public ActionResult TestReadSession()
        {           
            return View();
        }
    }

 View :

TestUpdateSession

<html>
<head>   
    <title>Test Update Session</title>
</head>
<body>
         Session value "@Session["TestSession"].ToString()"
</body>
</html>

TestReadSession

<html>
<head>   
    <title>Test Read Session</title>
</head>
<body>
         Session value "@Session["TestSession"].ToString()"
</body>
</html>

Scenario 1:

1st request TestController/TestReadSession

o/p : Session value “India”

2nd request TestController/TestUpdateSession

o/p : Session value “Pune”

Scenario 2:

1st request TestController/TestUpdateSession

o/p : Session value “Pune”

2nd request TestController/TestReadSession

o/p : Session value “India”

Conclusion : As I said in my previous post that in read-only session is updated at only request level. See here, 1st request has update the session value but it has not affected the next request.

One can set the session state behavior on the controller i.e Session State Behavior can ONLY be set at Controller level NOT at Action level. There are times where controller is Read-Only and there is/are some action which wants to update the session therefore it want Session behavior to be set as Default. Because of one action we cannot change the behavior of other actions. So one way is to write one NEW controller with session state as Default and move that action in that controller. But this logic is not very much feasible.

So, How to solve this problem??  Always remember one think “Every problem has the solution :)”. Just you need to find out.

Hint: As my site slogan say’s “You Rock, when your basics Rocks”, solution is hidden in basic of C#. OVERIDDING.

Will tell  Solution you in my next post “Session State at Action level“.  🙂