102

What is your way of passing data to Master Page (using ASP.NET MVC) without breaking MVC rules?

Personally, I prefer to code abstract controller (base controller) or base class which is passed to all views.

Łukasz Sowa
  • 1,287
  • 2
  • 11
  • 14

9 Answers9

77

If you prefer your views to have strongly typed view data classes this might work for you. Other solutions are probably more correct but this is a nice balance between design and practicality IMHO.

The master page takes a strongly typed view data class containing only information relevant to it:

public class MasterViewData
{
    public ICollection<string> Navigation { get; set; }
}

Each view using that master page takes a strongly typed view data class containing its information and deriving from the master pages view data:

public class IndexViewData : MasterViewData
{
    public string Name { get; set; }
    public float Price { get; set; }
}

Since I don't want individual controllers to know anything about putting together the master pages data I encapsulate that logic into a factory which is passed to each controller:

public interface IViewDataFactory
{
    T Create<T>()
        where T : MasterViewData, new()
}

public class ProductController : Controller
{
    public ProductController(IViewDataFactory viewDataFactory)
    ...

    public ActionResult Index()
    {
        var viewData = viewDataFactory.Create<ProductViewData>();

        viewData.Name = "My product";
        viewData.Price = 9.95;

        return View("Index", viewData);
    }
}

Inheritance matches the master to view relationship well but when it comes to rendering partials / user controls I will compose their view data into the pages view data, e.g.

public class IndexViewData : MasterViewData
{
    public string Name { get; set; }
    public float Price { get; set; }
    public SubViewData SubViewData { get; set; }
}

<% Html.RenderPartial("Sub", Model.SubViewData); %>

This is example code only and is not intended to compile as is. Designed for ASP.Net MVC 1.0.

Generic Error
  • 4,437
  • 6
  • 28
  • 26
  • 4
    This is the method recommended by Scott Gutherie, so I would have to agree. – Simon Fox Sep 21 '09 at 21:34
  • @Simon Fox - got a link to scottgu's recommendation? Couldn't find it. – orip Dec 15 '09 at 14:10
  • 4
    http://weblogs.asp.net/scottgu/archive/2007/12/06/asp-net-mvc-framework-part-3-passing-viewdata-from-controllers-to-views.aspx#5415732 – Dan Atkinson Mar 18 '10 at 12:05
  • Sorry. Having a little trouble understanding part of this. The constructor for the controller is passed an instance of IViewDataFactory but the system is expecting a parameterless constructor. I am also not familiar with that C# syntax (specifically the "MasterViewData, new()") for the interface. Can somebody please explain it or point me to a good resource. Thanks. – Jason May 12 '10 at 14:39
  • @Jason: Asp.net MVC allows you to specify a controller factory, which means you can use a dependency injection framework to instantiate controllers that take constructor parameters. `where T : MasterViewData, new()` tells the compiler that the Create method can only be called for types that extend `MasterViewData` (which `IndexViewData` does in this case), and which use a default or no-parameter constructor. In other words, I need to be able to say `MasterViewData data = new T();`. – StriplingWarrior Jul 16 '10 at 19:23
  • 5
    I like having strongly-typed models to work with, but I'm not a big fan of coupling the master data with all my other models and actions. Jumping into this thread a bit late, but I posted my approach to master data that keeps things more loose. – Todd Menier Aug 09 '10 at 22:01
  • Can anyone please help me understand, where/when is the value assigned to `Navigation` property? – IsmailS Mar 19 '11 at 18:26
60

I prefer breaking off the data-driven pieces of the master view into partials and rendering them using Html.RenderAction. This has several distinct advantages over the popular view model inheritance approach:

  1. Master view data is completely decoupled from "regular" view models. This is composition over inheritance and results in a more loosely coupled system that's easier to change.
  2. Master view models are built up by a completely separate controller action. "Regular" actions don't need to worry about this, and there's no need for a view data factory, which seems overly complicated for my tastes.
  3. If you happen to use a tool like AutoMapper to map your domain to your view models, you'll find it easier to configure because your view models will more closely resemble your domain models when they don't inherit master view data.
  4. With separate action methods for master data, you can easily apply output caching to certain regions of the page. Typically master views contain data that changes less frequently than the main page content.
Todd Menier
  • 37,557
  • 17
  • 150
  • 173
  • 3
    +1. Another advantage is that you can have the same view use different master pages depending on the current runtime state. – StriplingWarrior Oct 26 '10 at 15:45
  • 1
    I very much like this answer - the other approaches outlined do seem a bit over complicated. – Paddy Jan 05 '11 at 15:16
  • 2
    This is the most elegant solution in my opinion. – autonomatt Jan 05 '11 at 15:37
  • 1
    This solution seems best to me as well. Thanks a million! – JimDaniel Jan 15 '11 at 15:21
  • Sounds to me like there ends up being a "master controller" that contains all the actions used by `Html.RenderAction`, which benefits from DI and all that. Is that true? Is there any risk in allowing the possibility for "clever" users to request one of these actions directly? Any way to prevent it? – flipdoubt Mar 03 '12 at 17:17
  • 1
    This is a great way, but remember that you still have to specify routes to your "partial actions". See this answer http://stackoverflow.com/a/3553617/56621 – Alex from Jitbit Mar 05 '12 at 12:17
  • We're using MvcContrib and T4MVC, so we have no routing problems. Good tip though, jitbit. – flipdoubt Mar 09 '12 at 20:50
  • This approach is more natural - when I had same master-child issue, I've ended up with same approach. It's more logical to use compostion instead of inheritance in this case... – ajukraine Dec 13 '12 at 14:50
  • Agreed with advantages indicated on this answer, but there is one drawback in my opinion. It uses RenderAction, which breaks the "one-to-one" behavior I favor in MVC (1 http request => 1 route resolved => 1 controller => 1 action => 0 or 1 model fetched (potentially including sub-models) => 0 or 1 view rendered (potentially including partial-views using sub-models already fetched in the main view model). Using RenderAction means using many controllers and actions for a single page request. A bit comparable to using iframe in html in my opinion. This is why I prefer the answer of Generic Error. – Frédéric May 15 '13 at 10:30
20

EDIT

Generic Error has provided a better answer below. Please read it!

Original Answer

Microsoft has actually posted an entry on the "official" way to handle this. This provides a step-by-step walk-through with an explanation of their reasoning.

In short, they recommend using an abstract controller class, but see for yourself.

Community
  • 1
  • 1
Michael La Voie
  • 27,772
  • 14
  • 72
  • 92
7

Abstract controllers are a good idea, and I haven't found a better way. I'm interested to see what other people have done, as well.

Ian P
  • 12,840
  • 6
  • 48
  • 70
3

I did some research and came across these two sites. Maybe they could help.

ASP.NET MVC Tip #31 – Passing Data to Master Pages and User Controls

Passing Data to Master Pages with ASP.NET MVC

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
David Negron
  • 1,356
  • 2
  • 14
  • 23
2

I find that a common parent for all model objects you pass to the view is exceptionally useful.

There will always tend to be some common model properties between pages anyway.

Matt Mitchell
  • 40,943
  • 35
  • 118
  • 185
0

The other solutions lack elegance and take too long. I apologize for doing this very sad and impoverished thing almost an entire year later:

<script runat="server" type="text/C#">
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        MasterModel = SiteMasterViewData.Get(this.Context);
    }

    protected SiteMasterViewData MasterModel;
</script>

So clearly I have this static method Get() on SiteMasterViewData that returns SiteMasterViewData.

rasx
  • 5,288
  • 2
  • 45
  • 60
0

The Request.Params object is mutable. It's pretty easy to add scalar values to it as part of the request processing cycle. From the view's perspective, that information could have been provided in the QueryString or FORM POST. hth

0

I thing that another good way could be to create Interface for view with some Property like ParentView of some interface, so you can use it both for controls which need a reference to the page(parent control) and for master views which should be accessed from views.

dimarzionist
  • 18,517
  • 4
  • 22
  • 23