views:

587

answers:

3

When a user log in into my application i want to show his name throughout the whole application. I am using the asp.net MVC framework. But what i don't want is that is have to put in every controller something like:

ViewData["User"] = Session["User"];

This because you may not repeat yourself. (I believe this is the DRY [Don't Repeat Yourself] principle of OO programming.) The ViewData["User"] is on my masterpage. So my question is, what is a neat way to handle my ViewData["User"] on one place?

+3  A: 

You can do this fairly easily in either a controller base-class, or an action-filter that is applied to the controllers/actions. In either case, you get the chance to touch the request before (or after) the action does - so you can add this functionality there.

For example:

public class UserInfoAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(
        ActionExecutingContext filterContext)
    {
        base.OnActionExecuting(filterContext);
        filterContext.Controller.ViewData["user"] = "Foo";
    }
}
...
[HandleError, UserInfo]
public class HomeController : Controller
{...}

(can also be used at the action (method) level)


or with a common base-class:

public abstract class ControllerBase : Controller
{
    protected override void OnActionExecuting(
        ActionExecutingContext filterContext)
    {
        ViewData["user"] = "Bar";
        base.OnActionExecuting(filterContext);
    }
}

[HandleError]
public class HomeController : ControllerBase
{...}
Marc Gravell
Thnx, i'll try the base-class. But why overriding the OnActionExecuting() method? What does this method do? What is it's purpose?
Martijn
It executes just before your action (method) is executed. If you want, you could probably use OnActionExecuted instead, which executes immediately *after* your action is executed.
Marc Gravell
OKay, and because i am putting this in my base class, it affects al my actions within the controller?
Martijn
Correct. That is also true of the action-filter approach where you put the `[UserInfo]` at the class level (rather than the method level).
Marc Gravell
+2  A: 

Create a base class for your models with UserName property:

public abstract class BaseModel
{
    public string UserName { get; set; }
}

Create a base class for you controllers and override it's OnActionExecuted method. Within it check if model is derrived from BaseModel and if so, set it's UserName property.

public class ControllerBase : Controller
{
    protected override void OnActionExecuted(
        ActionExecutedContext filterContext)
    {
        var modelBase = ViewData.Model as ModelBase;

        if (modelBase != null)
        {
            modelBase.UserName = "foo";
        }

        base.OnActionExecuted(filterContext);
    }
}

Then you will be able to display user's UserName in the view like this:

<%= Html.Encode(Model.UserName) %>

See also:

Koistya Navin
"check if model is derrived from BaseModel and .." how do I do this? Can you give me an example?
Martijn
@Martijn, added an example.
Koistya Navin
I think I get your solution. But I isn't it a little complicated? Doesn't do the common base-class of Marc Gravell the same thing in a way?
Martijn
@Martijn, it allows you working with strongly typed model instead of ViewData[..] dictionary. With it you have intellisence support in your views etc etc.
Koistya Navin
+2  A: 

It's been a year, but I've just stumbled across this question and I believe there's a better answer.

Jimmy Bogard describes the solution described in the accepted answer as an anti-pattern and offers a better solution involving RenderAction: http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/06/18/the-filter-viewdata-anti-pattern.aspx

Paul Suart
+1 for the link Thnx.
Martijn