Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
794 views
in Technique[技术] by (71.8m points)

asp.net mvc 4 - Razor MVC, where to put global variables that's accessible across master page, partiview and view?

Hello Razor MVC Gurus:

Newbie question.

Background. I have a custom IIdentity that is set in an HttpModule before it gets to controller & views. To use it, i have to do

   MyIdentity myIdentity = (MyIdentity)((GenericPrincipal)context.User).Identity;
   MyComplexUser user = myIdentity.User;
   //user.name //user.location //user.username  //etc

The problem is, I use the object in different places such as

  • master layout
  • Some sub level nested layouts
  • Some partialviews
  • Some views

It really depends on what properties of "MyComplexUser" object the views need.

Currently, in the views, I have to do this really complicated casting to get to a property. For instance, if I want the "Name" of the user, I need to do

@(((MyComplexUser)(((MyIdentity)((GenericPrincipal)context.User).Identity).User)).Name)

I suppose I could put it in the controllers and then populate the ViewBag with a ViewBag.MyUser property, but then

  1. I don't like to use ViewBag. I prefer strongly typed objects
  2. If I use a strongly typed object ("MyUser") for views, then I have to popular all those models with a "MyUser" property. Feels a bit dirty? As I like to keep my models clean and be specific to the views they are involved with. Besides, it gets unnecessarily repetitive.
  3. In places like master_layout.cshtml or partialviews, how do you access "MyUser" if I put them in a controller?
  4. Use RenderAction and build partialviews for each User property is an overkill?

Thanks. Again, I'm a newbie at MVC 4, any suggestion is greatly appreciate it.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I'll explain a similar solution that works pretty well for me. With small changes, I believe that it will work for you (and others, hopefully) as well.

Basically, we'll be using inheritance.

Controllers

Let's create a custom base controller, such as

public class BaseController : Controller

and let's change our controllers to inherit from it, as

public class HomeController : BaseController

Models (ViewModels, I say)

You probably have lots of classes inside your Models folder, right? They act as DTOs from the controller to the views, right2? If you answered yes for both, then keep reading.

Let's create a base model class, such as public class BaseVM, and let's change our models to inherit from it, like public class HomeIndex : BaseVM

Important: your layout file (_Layout or whatsoever) must be strongly typed to BaseVM or a child of it.

The hook

Now that everything's beautifuly typed, let's use the request pipeline in our favor. At BaseController, you'll add a method that looks like this:

protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
    if (filterContext.Result is ViewResultBase)//Gets ViewResult and PartialViewResult
    {
        object viewModel = ((ViewResultBase)filterContext.Result).Model;

        if (viewModel != null && viewModel is BaseVM)
        {
            BaseVM baseVM = viewModel as BaseVM;

            baseVM.MyIdentity = (MyIdentity)((GenericPrincipal)context.User).Identity;
            //and so on...
        }
    }

    base.OnActionExecuted(filterContext);//this is important!
}

OnActionExecuted is called after the execution of the action but before the view rendering. That's exactly what we want.

I hope you got it already. =)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...