views:

1757

answers:

5

Hello Folks,

I was asking a related question but messed the title up and no-one would understand it. Since I am able now to ask the question more precisely, I decided to reformulate it in a new question and close the old one. Sorry for that.

So what I want to do is passing data (my custom user's nickname as stored in the db) to the LoginUserControl. This login gets rendered from the master page via Html.RenderPartial(), so what I really need to do is making sure that, say ViewData["UserNickname"] is present on every call. But I don't want to populate ViewData["UserNickname"] in each and every action of every controller, so I decided to use this approach and create an abstract base controller which will do the work for me, like so:

public abstract class ApplicationController : Controller
    {
        private IUserRepository _repUser;

        public ApplicationController()
        {
            _repUser = RepositoryFactory.getUserRepository();
            var loggedInUser = _repUser.FindById(User.Identity.Name); //Problem!
            ViewData["LoggedInUser"] = loggedInUser;
        }
    }

This way, whatever my deriving Controller does, the user information will already be present.

So far, so good. Now for the problem:

I can't call User.Identity.Name because User is already null. This is not the case in all of my deriving controllers, so this is specific for the abstract base controller.

I am setting the User.Identity.Name via FormsAuthentication at another place in the code, but I think this can't be the problem - afaik User.Identity.Name can be null, but not User itself.

It looks to me like the HttpContext is not available (since also null ;-) and that I am missing a simple yet important point here. Can anyone give me some hints? I would really appreciate it.

+1  A: 

Can you grab this using something like:

HttpContext currentContext = HttpContext.Current;
string userName = currentContext.User.Identity.Name;

Or is the HttpContext always empty??

Could you set the httpContext through the constructor of the abstract class? and use it this way?

keeney
+1  A: 

My guess would be that the Controller's base constructor is not filling in the User, but that it is only known later when the ControllerContext is set for the Controller. You should check this in the documentation about the lifecycle of an MVC application, (the one here will probably do, although it might be a bit out of date since it's for the preview version), or just check the source code of MVC.

from the code that I have of MVC (also a preview version, but that should be fine): (In Controller)

 public IPrincipal User {
            get {
                return HttpContext == null ? null : HttpContext.User;
            }
        }

...

public HttpContextBase HttpContext {
        get {
            return ControllerContext == null ? null : ControllerContext.HttpContext;
        }
    }

I don't see en an implementation of a default constructor in the code. That would prove that the ControllerContext is null at the time of construction.

So you should execute your code somewhere else.

Raymond Roestenburg
Strange, because `User` is null but `System.Web.HttpContext.Current.User` is not. MVC 1.0
Eduardo Molteni
A: 

Thanks Raimond. I was too tired to see the obvious. @Keeney: Yes the context is always null. Raimond pointed out why. Thanks anyway, I didn't see why too :-)

My current working solution (albeit not what I wanted) is a Attribute that I use to decorate all my controller actions. Here is the implementation:

public class MasterPageDataAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            base.OnActionExecuting(filterContext);
            IUserRepository _repUser = RepositoryFactory.getUserRepository();
            IPrincipal siteUser = filterContext.Controller.ControllerContext.HttpContext.User;
            User loggedInUser = null;

            if (siteUser == null || siteUser.Identity.Name == null)
            {
                //do nothing
            }
            else
            {
                loggedInUser = _repUser.findUserById(siteUser.Identity.Name);
            }
            filterContext.Controller.ViewData["LoggedInUser"] = loggedInUser ?? new User { Nickname = "Guest" };
        }
    }

I will be looking into how to get that code executed in a way that follows the DRY principle, since using attributes for that definitely means repeating oneself. Maybe some sort of interceptor (interesting idea) or hook might help.

Cheers for that.

Masterfu
A: 

I am doing this in a basecontroller implementation and it works as expected.

public abstract class BaseController : Controller
{
    public bool LoggedOn
    {
        get { return User.Identity.IsAuthenticated; }
    }
}

This always returns true or false for me so User != null

Schotime
+2  A: 

The answer to this problem is actually quite simple. I can't execute the code from within the constructor for reasons pointed out by Raimond, but I can do it outside the constructor.

So what I did was overriding onActionExecuting() in the base controller class (I created a custom Attribute for it, but just overriding the method should also work) and then do my user lookup from there.

Now it works as expected and I have no repeated code.

Masterfu
Thanks for posting how you solved that
jayrdub
My pleasure :-)
Masterfu