Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where should I attach a custom user-context Session wrapper in ASP.NET MVC3?

I have read many posts on Session-scoped data in MVC, but I am still unclear where is the right place to include a custom Session wrapper into the solution.

I want to get the Username of the current user from the IPrincipal, load additional information about that User and store it in the Session. Then I want to access that User data from the Controller and the View.

None of the following approaches seem to fit what I want to do.

Option 1 : Access the Session collection directly

Everyone seems to agree this is a bad idea, but honestly it seems like the simplest thing that works. However, it doesn't make the User available to the view.

public class ControllerBase : Controller {
   public ControllerBase() : this(new UserRepository()) {}
   public ControllerBase(IUserRepository userRepository) {
      _userRepository = userRepository;
   }
   protected IUserRepository _userRepository = null;
   protected const string _userSessionKey = "ControllerBase_UserSessionKey";
   protected User {
      get { 
         var user = HttpContext.Current.Session[_userSessionKey] as User;
         if (user == null) {
            var principal = this.HttpContext.User;
            if (principal != null) {
               user = _userRepository.LoadByName(principal.Identity.Name);
               HttpContext.Current.Session[_userSessionKey] = user;
            }
         }
         return user;
      }
   }
}

Option 2: Injecting the Session into the class constructor forum post

This option seems pretty good, but I am still not sure how to attach it to the Controller and the View. I could new-it-up in the Controller, but shouldn't it be injected as a dependency?

public class UserContext {
   public UserContext() 
       : this(new HttpSessionStateWrapper(HttpContext.Current.Session), 
              new UserRepository()) { } 

   public UserContext(HttpSessionStateBase sessionWrapper, IUserRepository userRepository) { 
      Session = sessionWrapper;
      UserRepository = userRepository; 
   } 

   private HttpSessionStateBase Session { get; set; }
   private IUserRepository UserRepository{ get; set; }

   public User Current { 
      get {
         //see same code as option one
      }
   }
}

Option 3 : Use Brad Wilson's StatefulStorage class

In his presentation Brad Wilson features his StatefulStorage class. It is a clever and useful set of classes which include interfaces and uses constructor injection. However, it seems to lead me down the same path as Option 2. It uses interfaces, but I couldn't use the Container to inject it because it relies on a static factory. Even if I could inject it, how does it get passed to the View. Does every ViewModel have to have a base class with a setable User property?

Option 4 : Use something similar to the Hanselman IPrincipal ModelBinder

I could add the User as a parameter to the Action method and use a ModelBinder to hydrate it from the Session. This seems like a lot of overhead to add it everywhere it is needed. Plus I would still have to add it to the ViewModel to make it available to the View.

public ActionResult Edit(int id, 
   [ModelBinder(typeof(IPrincipalModelBinder))] IPrincipal user)
{ ... }

I feel like I am overthinking this, but it also seems like there should be an obvious place to do this sort of thing. What am I missing?

like image 954
jedatu Avatar asked Feb 20 '11 23:02

jedatu


People also ask

How can we create and access session variables in ASP.NET MVC?

In ASP.NET MVC, you can create and access session variables using HttpContext. Current. Session. In ASP.NET 5, ASP.NET team implemented a middleware to support session management.

Can we use session in MVC?

ASP.NET MVC provides three ways (TempData, ViewData and ViewBag) to manage session, apart from that we can use session variable, hidden fields and HTML controls for the same. But like session variable these elements cannot preserve values for all requests; value persistence varies depending the flow of request.


1 Answers

My approach to Session:

Cover Session with interface:

public interface ISessionWrapper
{
    int SomeInteger { get; set; }
}

Implement interface using HttpContext.Current.Session:

public class HttpContextSessionWrapper : ISessionWrapper
{
    private T GetFromSession<T>(string key)
    {
        return (T) HttpContext.Current.Session[key];
    }

    private void SetInSession(string key, object value)
    {
        HttpContext.Current.Session[key] = value;
    }

    public int SomeInteger
    {
        get { return GetFromSession<int>("SomeInteger"); }
        set { SetInSession("SomeInteger", value); }
    }
}

Inject into Controller:

public class BaseController : Controller
{
    public ISessionWrapper SessionWrapper { get; set; }

    public BaseController(ISessionWrapper sessionWrapper)
    {
        SessionWrapper = sessionWrapper;
    }
}

Ninject dependency:

Bind<ISessionWrapper>().To<HttpContextSessionWrapper>()

You can pass some commonly used information using ViewData when you want to use it in master page and using view model in specific views.

like image 83
LukLed Avatar answered Oct 19 '22 07:10

LukLed