views:

279

answers:

6

I'm giving MVC another shot, and I feel like I'm learning freakin' HTML all over again.

So, stupid question: At the top of my master page, I've got a partial view (or similar - I'm using the string template view engine and so far I love it) which either displays a small login form (similar to what NewEgg has), or a message like

You are signed in as (Name). [Profile] | [Sign out]

Unfortunately, I'm having a brain cramp and can't determine the best way to get that data (username, id) into the ViewData collection without explicitly specifying it in every controller method, like

public ActionResult Index()
{
    ViewData["IsAuthenticated"] = Session["IsAuthenticated"];
    ViewData["user.firstname"] = User.FirstName;
    return View("login");
}

That's pretty annoying to have to replicate all over the place. My next option would be to create a method called PopulateCommonViewData() and just call that from every action method, but that also seems lousy.

Am I missing something here?

+1  A: 

I would use ASP.NET membership and just check state and get info from the page context methods in the masterpage. Makes it simple!

Dustin Laine
And if I need to do this with non-membership data?
David Lively
Either way, keeping that info in the masterpage is ideal in my opinion. I HATE rewriting even the simplest code, DRY all the way!
Dustin Laine
A: 

Do not put these data in ViewData or model at all, and store it in static properties. These static properties could wrap Session or Application state, the ASP.NET cache or the request context bag, whatever is appropriate. So you do not clutter your controller with this kind of global stuff.

markus
Static properties attached to *what*?
David Lively
A static class, like e. g. UserContext? You can easily access this from your views. This way you also get rid of the ugly "magic strings" all over the place in your views and your base controller.
markus
Sunny
markus
+1  A: 

Couple of options off the top of my head, use a common base controller that all your controllers inherit from which adds the info in the OnActionExecuting method/override or you could possibly use an action filter if it is not required globally...

Simon Fox
+5  A: 

Derive your controllers from a base controller. Then move your method creating common view data into the OnActionExecuting/OnActionExecuted override in that base controller.

 public class BaseController : Controller
 {
      public override void OnActionExecuting( ActionExecutingContext filterContext )
      {
           ViewData["IsAuthenticated"] = Request.IsAuthenticated;
           ViewData["user.firstname"] = User.FirstName;
      }
 }

 public class MyController : BaseController // and you're done
 {
    ...
 }
tvanfosson
For the life of me, I can't understand why the project template doesn't ship with a BaseController. *EVERYONE* I know does it this way except the Microsoft team.
Portman
A: 

Two approaches I could see taking here. One is to use custom action filter attributes to get the data you need into the view model after your controller action executes (the OnResultExecuted method on ActionFilterAttribute would probably do it).

The other is to utilize a base controller class, and override the OnActionExecuted method there.

Since it sounds like you are going to want this on all pages, it seems the latter option would be less maintenance going forward.

ckramer
A: 

How about a base ViewModel class which gets injected with the IUserContext & all the other ViewModels in the application derive from this ViewModel?

Somehow, I am not comfortable with the concept of doing: ViewData["magic_string"] = "magic"; pattern recommended here...

Perhaps I am missing a point & would love to know what?

HTH

Sunny