views:

175

answers:

3

I have this code sample:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%
    if (Request.IsAuthenticated) {
%>
        Welcome <b><%= Html.Encode(Page.User.Identity.Name) %></b>!

The catch is I am putting the user's id and not name into the 'username' field:

User user = _userRepository.Get(...);
FormsAuthentication.RedirectFromLoginPage(user.Id.ToString(), false);

The first code sample is included in the Master View, and so appears in every view. What I don't understand is how to pass that view the User model entity, since I am not calling it directly.

Edit: Is there a single point in processing an HTTP request where I can intervene to inject the User model object into the ViewData? I would not want to touch every controller+action to achieve this.

+1  A: 

The most common workaround for this is store the user in a session variable. I'd say it's also the most popular way even in ASP.NET MVC.

In a utility class or custom page/usercontrol base class:

const string _userSessionKey = "CurrentUser_";
public UserModelType CurrentUser
{
    var user = HttpContext.Current.Session[_userSessionKey] as UserModelType;
    if(user == null)
    {
        if(HttpContext.Current.User.Identity.IsAuthenticated)
        {
            //new UserService() or DependencyContainer.Resolve<IMembershipService>()
            user = new MembershipService().GetUserById(HttpContext.Current.User.Identity.Name);
            HttpContext.Current.Session[_userSessionKey] = user;
        }
    }

    return user;
}

I know!
This is too classic and probably not MVC-ish, but usually I see similar implementations.

The most important thing to keep this MVC friendly is having this code better called from a controller. You can use a partial controller that outputs a User Control or so (there is a method RenderAction I consider one of the best parts). So that the view is only passed the Name of the user or the user model not calling this directly. That's why a utility class here is much better than a base page/control class, but there are the options in general.

If you want the nicer stuff you should be looking for something like:

ASP.Net MVC Membership Starter Kit

Mohamed Meligy
But I would need to call this code from every controller then, violating DRY?
ripper234
No!You make it in a utility class, call it from some partial controller whose view is a user control.Then you call RenderAction<>() method in any view that needs to display this, typically in the master page not in a view.If you need to access the user for other purpose later from other controller you call the property of the utility class.
Mohamed Meligy
Do you have other concerns? Did you try it and get it working?
Mohamed Meligy
+1  A: 

What I've settled on is creating a master view model class that nearly every view-specific model derives from. The master model contains the information that every view may need. I try to keep this information very small and cache it in the session so that populating the model is not an intensive operation. The model implements an interface so that every view-specific model can also implement this interface and, thus, I can easily create a strongly-typed view that accepts the interface if I only care about the base information. I use a base controller for all of my controllers and the code to populate the base model is implemented on the base controller. Every specific view model has a constructor that takes the base model interface and copies the data to the local instance.

tvanfosson
+1  A: 

You could ensure every controller/page/control implements a base class in which you expose the model data you're after.

This tends to get painful, quite quickly, but it's cleaner to pass a ViewModel around than to be hitting the HttpContext from the page itself.

Edit: This is one of the few cases where I might be inclined to go with the ViewData["username"] and use that in the MasterPage, at least that way only the base class needs to populate this and you can avoid knowledge of authentication throughout your controllers.

TreeUK
Is there a single point in processing an HTTP request where I can intervene to inject the User model object into the ViewData? I would not want to touch every controller+action to achieve this.
ripper234