views:

89

answers:

3

Hi,

I'm trying to implement a custom user object in ASP.NET MVC 2. I've seen a solution where you can do some magic in Global.asax to turn Controller.User into another type, say CustomUser. But Controller.User is still an IPrincipal, which means I have to cast it to CustomUser every time I want to use it, and I don't like that at all.

Would it be considered wrong, or bad practice, to have a a base controller with a GetUser() method, where GetUser() calls a user repository, and uses Controller.User to fetch our own custom user object?

What I'm trying to do is just add a couple of properties to the user object.

+1  A: 

Would it be considered wrong, or bad practice, to have a a base controller with a GetUser() method, where GetUser() calls a user repository, and uses Controller.User to fetch our own custom user object?

I don't think so. This is the way I do it. ;)

jfar
Then how do you avoid the confusion of whether to use this.User or this.GetUser()? :)
Anders Ekdahl
@Anders Ekdahl, By the return type.
jfar
A: 

That is the way to do it, however you would want to minimise the amount you need to cast the user object as to minimise violation of the Liskov Substitution Principle: http://en.wikipedia.org/wiki/Solid_%28object-oriented_design%29

Rather than casting it every time, is there not something you can bury away in an ActionFilter?

amarsuperstar
You say "That is the way to do it", but which way do you mean? :) Sounds like you mean injecting the custom user into the Controller.User. I agree in that it sounds like the best solution, but I can't figure out how to avoid those pesky casts.
Anders Ekdahl
Oops, I should have been more clear, I meant using the base class and a GetUser method as you mentioned. My point was minimise the need for casting, e.g. if you are always casting to get the users' roles, then can you do this in an action filter instead and hide the fast you are using a custom object. If you really need the custom user object everywhere, then consider making an IUserService which returns the custom user and have this injected by your IoC container (you could declare this on a BaseController). This way the code is testable and you have no casts
amarsuperstar
Well, I don't have any casts with a GetUser() in a base controller. The casts are only necessary when you want a custom object in Controller.User, since that is typed to IPrincipal.Right now, I have a base controller, which has an IdentityLoader that is set after the IoC constructs the controller. The base controller has a GetIdentity() method, that just calls IdentityLoader.Load() if it hasn't already been called.
Anders Ekdahl
A: 

Here's what I would do:

In global.asax.cs

protected void Application_PostAuthorizeRequest()
{
    if (HttpContext.Current.User != null && HttpContext.Current.User.Identity != null && !string.IsNullOrEmpty(HttpContext.Current.User.Identity.Name))
    {
        HttpContext.Current.Items["User"] = userRepo.FetchByUsername(HttpContext.Current.User.Identity.Name);
    }
}

public static CustomUser CurrentUser
{
    get
    {
        return HttpContext.Current.Items["User"] as CustomUser;
    }
}

then you have a handy static with the current user in it. This is a dirty but effective way to do it.

Of course, really I would add the user into my IOC container and inject it into my controllers via an IOC enabled ControllerFactory. This is the 'correct' thing to do.

Whatever you do, don't use a base class! Using a static is probably more maintainable in the long run than creating an enormous base class with all the 'handy' things you need to get hold of.

mcintyre321
To me, a static call is worse than a base class. I agree with you that I don't like enormous base classes, but all our controllers needs to interact with the current user. The base class would just get a UserLoader injected from the IoC, and the GetUser() method would just proxy that call to the loader.
Anders Ekdahl
One day you'll end up needing another service in most, but not all controllers. Then some wag will add it in to the base class anyway and lo! The monster base class is born!If you want to do it 'properly' use a ControllerFactory. http://code.google.com/p/autofac/wiki/MvcIntegration
mcintyre321
I'm already using a controller factory, and using StructureMap to inject dependencies. I don't like injecting the user object, but I could very well inject the UserLoader. But then all my controllers would depend on the UserLoader class, the User class, and the fact that UserLoader.Load() takes a Controller object. Instead, my controllers only depend on this.GetUser() returning a User.
Anders Ekdahl
Just inject the user. You won't regret it. Also, a lot easier to test than a base class.
mcintyre321