Heero Yuy
Heero Yuy

Reputation: 614

How can I set User data in one place in ASP.NET MVC 5

        public class CompanyController : Controller
        {
                    private ApplicationDbContext _dbContext;
                    //private ApplicationUser _applicationUser;

                    public CompanyController()
                    {
                        _dbContext = new ApplicationDbContext();

                       //I could have set _applicationUser here with
                       //_applicationUser = _dbContext.Users.Find(User.Identity.GetUserId()); 
                       //but User object is not yet avaialbe in constructor
                    }

                    public ActionResult Index()
                    {
                        var user = _dbContext.Users.Find(User.Identity.GetUserId());
                        ViewBag.Title = "Company Details";

                       //process user here

                        return View();
                    }

                   public ActionResult Page()
                    {
                        var user = _dbContext.Users.Find(User.Identity.GetUserId());
                        ViewBag.Title = "Page Details";

                       //process user here

                        return View();
                    }

                  // and I still have many ActionResult that needs user object
        }

This code is becoming redundant

var user = _dbContext.Users.Find(User.Identity.GetUserId());

I want to know how I can set the user object in one area of the controller so I don't have to repeat it in every action since constructor part in not an option.

Upvotes: 0

Views: 77

Answers (2)

NightOwl888
NightOwl888

Reputation: 56909

You can use a custom IControllerFactory to put this common code in one place (making it DRY). Coupled with a custom IUserEntityInjectable interface, it can be injected only on controllers that need it and eliminate any of the drawbacks associated with using a base class for this purpose. Note that I used the name UserEntity here because the Controller class already has a User property, so we need a different name.

public interface IUserEntityInjectable
{
    User UserEntity { get; set; }
}

public class UserEntityInjector : DefaultControllerFactory
{
    public override IController CreateController(RequestContext requestContext, string controllerName)
    {
        var controller = base.CreateController(requestContext, controllerName);

        if (controller is IUserEntityInjectable)
        {
            using (var dbContext = new ApplicationDbContext())
            {
                ((IUserEntityInjectable)controller).UserEntity = dbContext.Users.Find(requestContext.HttpContext.User.Identity.GetUserId());
            }
        }

        return controller;
    }
}

Usage

Simply wire in your custom IControllerFactory...

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);

        ControllerBuilder.Current.SetControllerFactory(new UserEntityInjector());
    }
}

Then it is just a matter of implementing IUserEntityInjectable on those controllers that need it.

public class CompanyController : Controller, IUserEntityInjectable
{
    public User UserEntity { get; set; }

    // Other code here...
}

The User entity will then automatically be looked up and populated if (and only if) IUserEntityInjectable is implemented in the controller.

If you need more fine grained control, you can implement an ActionFilterAttribute to populate the user only on specific actions by decorating the action with your custom attribute.

Upvotes: 1

Brian Mains
Brian Mains

Reputation: 50728

Once logged in, you could store the object in session:

var user = _dbContext.Users.Find(User.Identity.GetUserId());
HttpContext.Session["CurrentUser"] = user;

And then pull from session the user's details. I will often wrap this in a common utility, so I can change the underlying storage structure if needed, or can even repopulate the session if session dies (by pulling the user info from the authentication cookie) if useful... Just depends on your use case.

You can even create a custom ASP.NET controller base class and add a User property there, which pulls the current user in. As long as all your controllers inherit from this class, it will be accessible:

public abstract class BaseController : Controller
{
   public User User { get { .. } };
}

Putting the DB code, or the session alternative, in there and returning the user would make it accessible throughout.

Upvotes: 1

Related Questions