170

I am writing a web application that will allow a user to browse to multiple web pages within the website making certain requests. All information that the user inputs will be stored in an object that I created. The problem is that I need this object to be accessed from any part of the website and I don't really know the best way to accomplish this. I know that one solution is to use session variables but I don't know how to use them in asp .net MVC. And where would I declare a session variable? Is there any other way?

N30
  • 3,463
  • 4
  • 34
  • 43
Draco
  • 16,156
  • 23
  • 77
  • 92

10 Answers10

129

I would think you'll want to think about if things really belong in a session state. This is something I find myself doing every now and then and it's a nice strongly typed approach to the whole thing but you should be careful when putting things in the session context. Not everything should be there just because it belongs to some user.

in global.asax hook the OnSessionStart event

void OnSessionStart(...)
{
    HttpContext.Current.Session.Add("__MySessionObject", new MySessionObject());
}

From anywhere in code where the HttpContext.Current property != null you can retrive that object. I do this with an extension method.

public static MySessionObject GetMySessionObject(this HttpContext current)
{
    return current != null ? (MySessionObject)current.Session["__MySessionObject"] : null;
}

This way you can in code

void OnLoad(...)
{
    var sessionObj = HttpContext.Current.GetMySessionObject();
    // do something with 'sessionObj'
}
John Leidegren
  • 59,920
  • 20
  • 131
  • 152
  • 35
    If ASP MVC is being used then it is preferable to not use the actual Session object from HttpContext.Current.Session but to use the new HttpSessionStateWrapper&HttpSessionStateBase from System.Web.Abstractions.dll then use a Factory or DI to get the Session. – Paul Feb 18 '09 at 16:04
  • Gah! I read this too late! I already wrote my own wrapper class to generically wrap Session or Context-type data! – Ogre Psalm33 Oct 06 '09 at 20:06
  • 6
    How do you assign something to the session variable? (as opposed to just accessing) – raklos Jan 29 '11 at 18:44
  • 31
    For people trying to figure out what the "OnSessionStart" event is and how you "hook" it, see http://stackoverflow.com/questions/1531125/asp-net-mvc-onsessionstart-event – Cephron May 04 '11 at 21:18
  • your GetMySessionObject method has no return type – ChrisCa Apr 16 '13 at 09:55
  • If you change a field in the sessionobject, but don't write it back, won't the value be lost, especially when using out of proc session state (i.e. serialized and stored on another machine) – David d C e Freitas Nov 04 '13 at 03:16
  • @DavidFreitas Normally, I think the object persistence is handled for you but it obviously depends on what provider you use. However, a provider cannot prevent a data race (let's say you have more than one request that binds to the same session state, the order in which these occur will affect the outcome). Regardless, session state in the ASP.net way of doing things ought to be avoided infavour of more decentralized alternatives. – John Leidegren Nov 04 '13 at 06:43
  • 5
    @Paul Are you able to provide an example? I can't seem to find any examples of using HttpSessionStateWrapper. – Joseph Woodward Dec 29 '13 at 00:54
  • 1
    var theWrapper = new HttpSessionStateWrapper(HttpContext.Current.Session); then pretty much use it like you would the session state object .At least, that's how I've used it so far. Using the wrapper solved a lot of issues where session variables were not present. – Sinaesthetic Apr 24 '14 at 22:45
  • There is even better way of handling session variables. Check my answer. – Ajay Kelkar Jul 06 '14 at 07:08
  • 5
    @AjayKelkar This comment thread suggested "If ASP MVC is being used then it is preferable to not use the actual Session object from HttpContext.Current.Session but to use the new HttpSessionStateWrapper&HttpSessionStateBase" which suggests your answers it not better – Paul C Dec 05 '14 at 15:44
53

The answer here is correct, I however struggled to implement it in an ASP.NET MVC 3 app. I wanted to access a Session object in a controller and couldn't figure out why I kept on getting a "Instance not set to an instance of an Object error". What I noticed is that in a controller when I tried to access the session by doing the following, I kept on getting that error. This is due to the fact that this.HttpContext is part of the Controller object.

this.Session["blah"]
// or
this.HttpContext.Session["blah"]

However, what I wanted was the HttpContext that's part of the System.Web namespace because this is the one the Answer above suggests to use in Global.asax.cs. So I had to explicitly do the following:

System.Web.HttpContext.Current.Session["blah"]

this helped me, not sure if I did anything that isn't M.O. around here, but I hope it helps someone!

Tomasz Iniewicz
  • 4,379
  • 6
  • 42
  • 47
21

Because I dislike seeing "HTTPContext.Current.Session" about the place, I use a singleton pattern to access session variables, it gives you an easy to access strongly typed bag of data.

[Serializable]
public sealed class SessionSingleton
{
    #region Singleton

    private const string SESSION_SINGLETON_NAME = "Singleton_502E69E5-668B-E011-951F-00155DF26207";

    private SessionSingleton()
    {

    }

    public static SessionSingleton Current
    {
        get
        {
            if ( HttpContext.Current.Session[SESSION_SINGLETON_NAME] == null )
            {
                HttpContext.Current.Session[SESSION_SINGLETON_NAME] = new SessionSingleton();
            }

            return HttpContext.Current.Session[SESSION_SINGLETON_NAME] as SessionSingleton;
        }
    }

    #endregion

    public string SessionVariable { get; set; }
    public string SessionVariable2 { get; set; }

    // ...

then you can access your data from anywhere:

SessionSingleton.Current.SessionVariable = "Hello, World!";
Dead.Rabit
  • 1,965
  • 1
  • 28
  • 46
  • 2
    So this class has two responsibilities: maintain a single instance, and store variables... I would use an IOC container to have a singleton. – Jowen Mar 19 '13 at 12:32
  • 1
    If you already have one setup I'd probably make a fully fledged inject-able session service too, though, consistencies probably the biggest advantage, I'd be more inclined to use this code for small featureset webapps.. webwizards if you will. – Dead.Rabit Mar 19 '13 at 15:40
  • I am afraid that is is a bad practice because static variables can be shared across sessions. – vhakti Jan 04 '21 at 16:11
15

Well, IMHO..

  1. never reference a Session inside your view/master page
  2. minimize your useage of Session. MVC provides TempData obj for this, which is basically a Session that lives for a single trip to the server.

With regards to #1, I have a strongly typed Master View which has a property to access whatever the Session object represents....in my instance the stongly typed Master View is generic which gives me some flexibility with regards to strongly typed View Pages

ViewMasterPage<AdminViewModel>

AdminViewModel
{
    SomeImportantObjectThatWasInSession ImportantObject
}

AdminViewModel<TModel> : AdminViewModel where TModel : class
{
   TModel Content
}

and then...

ViewPage<AdminViewModel<U>>
E Rolnicki
  • 1,677
  • 2
  • 17
  • 26
14

If you are using asp.net mvc, here is a simple way to access the session.

From a Controller:

{Controller}.ControllerContext.HttpContext.Session["{name}"]

From a View:

<%=Session["{name}"] %>

This is definitely not the best way to access your session variables, but it is a direct route. So use it with caution (preferably during rapid prototyping), and use a Wrapper/Container and OnSessionStart when it becomes appropriate.

HTH

John x
  • 4,031
  • 8
  • 42
  • 67
robertz
  • 758
  • 6
  • 11
  • 2
    hm.. Which is best way? I should pass data to ViewState from Session on controller, shouldn't? – RredCat Feb 10 '11 at 08:50
  • 2
    and could you explain constrictions of this method? – RredCat Feb 10 '11 at 09:43
  • 1
    I think he meant that it's best to have read/write methods. Depending on the concurrency/thread usage, you might also need locks in those read/write methods to avoid a race condition. – DeepSpace101 Feb 07 '12 at 01:44
7

My way of accessing sessions is to write a helper class which encapsulates the various field names and their types. I hope this example helps:

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.SessionState;

namespace dmkp
{
    /// <summary>
    /// Encapsulates the session state
    /// </summary>
    public sealed class LoginInfo
    {
        private HttpSessionState _session;
        public LoginInfo(HttpSessionState session)
        {
            this._session = session;
        }

        public string Username
        {
            get { return (this._session["Username"] ?? string.Empty).ToString(); }
            set { this._session["Username"] = value; }
        }

        public string FullName
        {
            get { return (this._session["FullName"] ?? string.Empty).ToString(); }
            set { this._session["FullName"] = value; }
        }
        public int ID
        {
            get { return Convert.ToInt32((this._session["UID"] ?? -1)); }
            set { this._session["UID"] = value; }
        }

        public UserAccess AccessLevel
        {
            get { return (UserAccess)(this._session["AccessLevel"]); }
            set { this._session["AccessLevel"] = value; }
        }

    }
}
Daniel
  • 71
  • 1
  • 2
  • I like your answer... could you elaborate further what is going on... and why this is a better approach opposed to the other answers on this thread. – Chef_Code Sep 16 '16 at 16:58
7

Although I don't know about asp.net mvc, but this is what we should do in a normal .net website. It should work for asp.net mvc also.

YourSessionClass obj=Session["key"] as YourSessionClass;
if(obj==null){
obj=new YourSessionClass();
Session["key"]=obj;
}

You would put this inside a method for easy access. HTH

DotNET
  • 309
  • 2
  • 7
7

There are 3 ways to do it.

  1. You can directly access HttpContext.Current.Session

  2. You can Mock HttpContextBase

  3. Create a extension method for HttpContextBase

I prefer 3rd way.This link is good reference.

Get/Set HttpContext Session Methods in BaseController vs Mocking HttpContextBase to create Get/Set methods

Community
  • 1
  • 1
Mangesh
  • 3,987
  • 2
  • 31
  • 52
6

Great answers from the guys but I would caution you against always relying on the Session. It is quick and easy to do so, and of course would work but would not be great in all cicrumstances.

For example if you run into a scenario where your hosting doesn't allow session use, or if you are on a web farm, or in the example of a shared SharePoint application.

If you wanted a different solution you could look at using an IOC Container such as Castle Windsor, creating a provider class as a wrapper and then keeping one instance of your class using the per request or session lifestyle depending on your requirements.

The IOC would ensure that the same instance is returned each time.

More complicated yes, if you need a simple solution just use the session.

Here are some implementation examples below out of interest.

Using this method you could create a provider class along the lines of:

public class CustomClassProvider : ICustomClassProvider
{
    public CustomClassProvider(CustomClass customClass)
    { 
        CustomClass = customClass;
    }

    public string CustomClass { get; private set; }
}

And register it something like:

public void Install(IWindsorContainer container, IConfigurationStore store)
{
    container.Register(
            Component.For<ICustomClassProvider>().UsingFactoryMethod(
                () => new CustomClassProvider(new CustomClass())).LifestylePerWebRequest());
    }
shenku
  • 11,969
  • 12
  • 64
  • 118
4

You can use ViewModelBase as base class for all models , this class will take care of pulling data from session

class ViewModelBase 
{
  public User CurrentUser 
  {
     get { return System.Web.HttpContext.Current.Session["user"] as User };
     set 
     {
        System.Web.HttpContext.Current.Session["user"]=value; 
     }
  }
}

You can write a extention method on HttpContextBase to deal with session data

T FromSession<T>(this HttpContextBase context ,string key,Action<T> getFromSource=null) 
{
    if(context.Session[key]!=null) 
    {
        return (T) context.Session[key];
    }
  else if(getFromSource!=null) 
  {
    var value = getFromSource();
   context.Session[key]=value; 
   return value; 
   }
  else 
  return null;
}

Use this like below in controller

User userData = HttpContext.FromSession<User>("userdata",()=> { return user object from service/db  }); 

The second argument is optional it will be used fill session data for that key when value is not present in session.

Ajay Kelkar
  • 4,591
  • 4
  • 30
  • 29