Tutorial :ModelFactory in ASP.NET MVC to solve 'RenderPartial' issue



Question:

The 'RenderPartial()' method in ASP.NET MVC offeres a very low level of functionality. It does not provide, nor attempt to provide a true 'sub-controller' model *.

I have an increasing number of controls being rendered via 'RenderPartial()'. They fall into 3 main categories :

1) Controls that are direct descendants of a specific page that use that page's model

2) Controls that are direct descendants of a specific page that use that page's model with an additional key of some type. Think implementation of 'DataRepeater'.

3) Controls that represent unrelated functionality to the page they appear on. This could be anything from a banner rotator, to a feedback form, store locator, mailing list signup. The key point being it doesn't care what page it is put on.

Because of the way the ViewData model works there only exists one model object per request - thats to say anything the subcontrols need must be present in the page model.

Ultimately the MVC team will hopefully come out with a true 'subcontroller' model, but until then I'm just adding anything to the main page model that the child controls also need.

In the case of (3) above this means my model for 'ProductModel' may have to contain a field for 'MailingListSignup' model. Obviously that is not ideal, but i've accepted this at the best compromise with the current framework - and least likely to 'close any doors' to a future subcontroller model.

The controller should be responsible for getting the data for a model because the model should really just be a dumb data structure that doesn't know where it gets its data from. But I don't want the controller to have to create the model in several different places.

What I have begun doing is creating a factory to create me the model. This factory is called by the controller (the model doesn't know about the factory).

public static class JoinMailingListModelFactory {            public static JoinMailingListModel CreateJoinMailingListModel() {                return new JoinMailingListModel()              {                  MailingLists = MailingListCache.GetPartnerMailingLists();              };          }      }     

So my actual question is how are other people with this same issue actually creating the models. What is going to be the best approach for future compatibility with new MVC features?


  • NB: There are issues with RenderAction() that I won't go into here - not least that its only in MVCContrib and not going to be in the RTM version of ASP.NET-MVC. Other issues caused sufficent problems that I elected not to use it. So lets pretend for now that only RenderPartial() exists - or at least that thats what I've decided to use.


Solution:1

One approach I've seen for this scenario is to use an action-filter to populate the data for the partial view - i.e. subclass ActionFilterAttribute. In the OnActionExecuting, add the data into the ViewData. Then you just have to decorate the different actions that use that partial view with the filter.


Solution:2

Instead of adding things like MailingListSignup as a property of your ProductModel, encapsulate both at the same level in a class like ProductViewModel that looks like:

public class ProductViewModel() {      public ProductModel productModel;      public MailingListSignup signup;  }  

Then get your View to be strongly-typed to the ProductViewModel class. You can access the ProductModel by calling Model.productModel, and you can access the signup class using Model.signup.

This is a loose interpretation of Fowler's 'Presentation Model' (http://martinfowler.com/eaaDev/PresentationModel.html), but I've seen it used by some Microsoft devs, such as Rob Conery and Stephen Walther.


Solution:3

There's a RenderPartial overload I use that let's you specify a new ViewData and Model:

RenderPartial code

If you look at the previous link of the MVC source code, as well as the following (look for RenderPartialInternal method):

RenderPartialInternal code

you can see that if basically copies the viewdata you pass creating a new Dictionary and sets the Model to be used in the control. So the page can have a Model, but then pass a different Model to the sub-control.

If the sub-controls aren't referred directly from the main View Model, you could do the trick Marc Gravell mentions to add your custom logic.


Solution:4

One method I tried was to use a strongly typed partial view with an interface. In most situations an agregated ViewModel is the better way, but I still want to share this.

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IMailingListSignup>" %>  

The Viewmodel implements the interface

 public class ProductViewModel:IMailingListSignup  

Thats not perfect at all but solves some issues: You can still easily map properties from your route to the model. I am not shure if you can have a route parameter map to the properties of MailingListSignup otherwise.

You still have the problem of filling the Model. If its not to late I prefer to do it in OnActionExecuted. I dont see how you can fill a Model in OnActionExecuting.


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »