An Introduction to the Derived Type Model Binder

The DerivedTypeModelBinder and associated code enables you to both render and model-bind instances in your object graph that derive from a common base. Whereas the DefaultModelBinder in Mvc traverses the object graph and attempts to instantiate literal types to build up your instance, DerivedTypeModelBinder enables you to declaratively signal when a common base (interface or class) should look to derived types for actual instantiation. In addition, DerivedTypeModelBinder introduces a new RenderPartial variation called RenderTypedPartial<TModel>(TModel instance) that inspects the model for it's derived type and renders the partial according to name convention. The naming convention is fully configurable and is described below along with discussion on how the pieces work together.

Setting the Stage - Our Object that Refers to a Common Base Type

Let’s say I’m working for a furniture manufacturer that makes a wide range of products – from case goods to upholstered furniture. On our website, we want to allow the user to configure a given product in one step and simplify scenarios for generating our page content.

We’ll start with a type called DynamicItem that contains multiple parts:

   1: public class DynamicItem
   2: { 
   3:     public string ItemName { get; set; }
   4:  
   5:     public IConfigurablePart[] ConfigurableParts {get; set;}
   6: }


The key with respect to rendering DynamicItem is the ability to render the various flavors of IConfigurablePart. Before we get into that, let’s look at the definition IConfigurablePart and its derivatives next.

   1: public interface IConfigurablePart
   2: {
   3:     string ItemType { get; set; }
   4: }
   5:  
   6: public class FurnitureLeg : IConfigurablePart
   7: {
   8:     #region IConfigurablePart Members
   9:  
  10:     public string ItemType { get; set; }
  11:  
  12:     #endregion
  13:  
  14:     public string LegStyle { get; set; }
  15:     public int Height { get; set; }
  16: }
  17:  
  18: public class RoundFurnitureTop : IConfigurablePart
  19: {
  20:     #region IConfigurablePart Members
  21:  
  22:     public string ItemType { get; set; }
  23:  
  24:     #endregion
  25:  
  26:     public string WoodStyle { get; set; }
  27:     public string Diameter { get; set; }
  28: }
  29:  
  30: public class UpholsteredPart : IConfigurablePart
  31: {
  32:     #region IConfigurablePart Members
  33:  
  34:     public string ItemType { get; set;}
  35:  
  36:     #endregion
  37:  
  38:     public bool Plaid { get; set; }
  39: }


Now we have a set of options for IConfigurablePart that help us demonstrate the mechanism (even if they are greatly simplified).

Declaring Type Variations for the Model Binding Process

Our first step is declaring the possible options for IConfigurablePart and there are two approaches.

The first approach follows the KnownTypeAttribute pattern established in WCF by using our attribute called ‘DerivedTypeBinderAwareAttribute':

   1: [DerivedTypeBinderAware(typeof(FurnitureLeg))]
   2: [DerivedTypeBinderAware(typeof(RoundFurnitureTop))]
   3: [DerivedTypeBinderAware(typeof(UpholsteredPart))]
   4: public interface IConfigurablePart
   5: {
   6:     string ItemType { get; set; }
   7: }


The second approach that can be used in place of the attribute method – declaring the definitions at a single point in the application (i.e. application start).

   1: protected void Application_Start()
   2: {
   3:     DerivedTypeModelBinderCache.RegisterDerivedTypes(typeof(IConfigurablePart),
   4:                                          new[]
   5:                                                          {
   6:                                                              typeof (FurnitureLeg), 
   7:                                                              typeof (RoundFurnitureTop),
   8:                                                              typeof (UpholsteredPart)
   9:                                                          });


The advantage to the second approach is avoiding the reflection hit – but keep in mind that the BinderCache is the common mechanism so the reflection cost is only hit once. It can also be helpful when you do not have control over the set of objects – perhaps they live in a separate assembly provided to you.

Setting Up the Controller

Next, let’s setup the controller with an item that we can walk through. Not much here – just a basic instantiation and passthrough to our strongly typed view.

   1: public ActionResult Configure()
   2: {
   3:     // create a simple instance for the sake of demonstration
   4:  
   5:     return View(new DynamicItem
   6:                     {
   7:                         ItemName = "Side Table",
   8:                         
   9:                         ConfigurableParts =
  10:                             new IConfigurablePart[] {
  11:                             new RoundFurnitureTop
  12:                                 {Diameter = "50in", WoodStyle = "Maple", ItemType = "standardRound"},
  13:                             new FurnitureLeg 
  14:                                 {Height = 36, LegStyle = "Queen Anne", ItemType = "StandardLeg"} }
  15:                     });
  16: }

Setting Up the View

Our next step is set up the base view to be used to render the root object. First off, we’ll use MvcContrib’s ModelView… page/partial classes as these handle binding context across the views quite nicely.

   1: <h2>Configure</h2>
   2: <%using (Html.BeginForm())
   3:  
   4: <div class="editor-label">
   5:     <%=this.Label(m => m.ItemName)%>
   6: </div>
   7: <div class="editor-field">
   8:     <%=this.TextBox(m => m.ItemName)%>
   9:     <%=this.ValidationMessage(m => m.ItemName)%>
  10: </div>
  11:  
  12: <%for (var i = 0; i < Model.ConfigurableParts.Count(); i++){%>
  13: <div class='config-part'>
  14:     <%
  15:      this.RenderTypedPartial(m => m.ConfigurableParts[i]);%>
  16: </div>
  17: <%}%>
  18: <input type="submit" value="Submit" />


Note the use of the for() loop – a Repeater could be used here as long as it integrates with the binder context handling of MvcContrib. The for loop works nicely because it gives the expression handed to RenderTypedPartial a complete handle over the object graph traversal.

Using RenderTypedPartial to Target the Appropriate Partial At Runtime

The RenderTypedPartial takes an instance, determines its type and then renders a partial using a naming convention. This naming convention by default is ‘{TypeName}TypedPartial’. Therefore, and instance of type ‘RoundFurnitureTop’ would resolve to ‘RoundFurnitureTopTypedPartial’. This naming convention is stored in MvcContrib.FluentHtml.PartialNameConventionService.PartialNameConvention. You can modify this in startup if you so choose.

Internally, RenderTypedPartial injects a 'TypeStamp' into the page for you. This permits the DerivedTypeModelBinder to identify the proper object to instantiate at bind-time. Using RenderTypedPartial means you do not need to manually call TypeStamp() in your partial, but if you use another mechanism to display your type-specific content then you will need to do so.

The next step is to set up the partials – and as such I am deriving the partial from ModelViewUserControl from MvcContrib.FluentHtml. The piece to pay attention to here is the ‘TypeStamp()” extension I added to my branch of MvcContrib. It actually wraps a hidden field, but places the type context in the page that can be used by the DerivedTypeModel binder to instantiate the proper item.

First, the code for ‘FurnitureLegTypePartial’

   1: <h2>Furniture Leg</h2>
   2: 
   3: <div class="editor-label">
   4:     <%= this.Label(m => m.Height) %>
   5: </div>
   6: <div class="editor-field">
   7:     <%= this.TextBox(m => m.Height)%>
   8:     <%= this.ValidationMessage(m => m.Height)%>
   9: </div>
  10: <div class="editor-label">
  11:     <%= this.Label(m => m.LegStyle) %>
  12: </div>
  13: <div class="editor-field">
  14:     <%= this.TextBox(m => m.LegStyle)%>
  15:     <%= this.ValidationMessage(m => m.LegStyle)%>
  16: </div>


And next, the code for ‘RoundFurnitureTopTypePartial’

   1: <h2>Round Table Top</h2>
   2: 
   3: <div class="editor-label">
   4:     <%= this.Label(m => m.Diameter) %>
   5: </div>
   6: <div class="editor-field">
   7:     <%= this.TextBox(m => m.Diameter)%>
   8:     <%= this.ValidationMessage(m => m.Diameter)%>
   9: </div>
  10: <div class="editor-label">
  11:     <%= this.Label(m => m.WoodStyle) %>
  12: </div>
  13: <div class="editor-field">
  14:     <%= this.TextBox(m => m.WoodStyle)%>
  15:     <%= this.ValidationMessage(m => m.WoodStyle)%>
  16: </div>

Registering Our Base Type with the Model Binding Process

This next step is key – we need to register the invariant type in these binding situations to be handled by the DerivedTypeModelBinder. This can be done using any of the regular options, and we’ll use the direct method on startup in this scenario. This is instructing ModelBinding to use DerivedTypeModelBinder whenenver it runs into an IConfigurablePart reference.

   1: ModelBinders.Binders.Add(typeof(IConfigurablePart),
   2:     new DerivedTypeModelBinder());

Typing it altogether on HttpPost

The final step is wiring up our form submit to the controller. We do this in the normal MVC convention. Because we are following conventions with the DefaultModelBinder and haven’t changed the way validation is being performed, all model validation applies even to our derived types. In this case, we see that DynamicItem is fully populated and any validation errors are thrown through the regular mechanism.

   1: [HttpPost]
   2: public ActionResult Configure(DynamicItem dynamicItem)
   3: {
   4:     if( ModelState.IsValid)
   5:     {
   6:         _repository.Save(dynamicItem);
   7:         return RedirectToAction("SaveLanding");
   8:     }
   9:     return View(dynamicItem);
  10: }

Summary

This gives us the ability to render and model-bind much more flexible object structures in a common and reusable manner. To recap the key points, we must:
  1. Declare the type variations using DerivedTypeBinderAwareAttribute or the RegisterDerivedTypes mechanism mentioned above.
  2. Ensure that views are rendered by the appropriate derived type at runtime and provide the typestamp in the view output. RenderTypedPartial takes care of both of these conditions automatically.
  3. Register the common base type with Mvc's ModelBinders.Binders collection.

Build out the appropriate views and you are on your way to having a completely flexible rendering of your object graphs.

Last edited Mar 22, 2010 at 6:17 AM by sdhebert, version 6

Comments

lynnae Sep 29, 2011 at 4:24 PM 
I'm completely new to this but just viewed the mvcConf presentation and it may be helpful for the MVC 3 changes:
http://channel9.msdn.com/Series/mvcConf/mvcConf-2-Steve-Hebert-ModelBinding-derived-types-using-the-DerivedTypeModelBinder-in-MvcContrib

vzgromozdun Aug 24, 2011 at 8:32 AM 
It seems like there is no RenderTypedPartial in mvccontrib 3 :-(

tkerwood Jul 27, 2011 at 8:04 AM 
@sdhebert
This looks great, but i cant get it working. I am using mvc 3 and cannot resolve "@Html.RenderTypedPartial(m => m.ConfigurableParts[i])". Looks like a few other people have had this issue. Can anyone tell me where i am going wrong?
I have tried adding @inherits MvcContrib.MvcFluentHtml.ModelViewPage<MvcContribProto.Models.DynamicItem> to the top of my view, but cant resolve that either.

geoffreys May 16, 2011 at 11:29 AM 
Works per sdherbet's instruction on 15/Feb -- except registering types for reflection via [DerivedTypeBinderAware(...)] which generates "The given key was not present in the dictionary." invoking .TimeStamp(); in the view. Types registered directly via DerivedTypeModelBinderCache.RegisterDerivedType(...) work fine.

No need to inherit MvcContrib.FluentHtml.ModelViewPage / ModelViewUsercontrol

sdhebert Feb 15, 2011 at 12:01 AM 
For mvc 3, Check out the sample project - it's inside the DerivedTypeModelBinder controller. The only reference you need is the single MvcContrib core dll - which includes the derived type model binder and supporting extension methods.

vzgromozdun Oct 19, 2010 at 3:16 PM 
here is my fix to make validation works with RenderTypePartial():

private ViewDataDictionary GetViewData()
{
TPartialViewModel model = null;
if (modelExpression != null)
{
model = modelExpression.Compile().Invoke(view.ViewModel);
}

if (model == null)
{
return viewData == null ? new ViewDataDictionary(view.ViewData) : new ViewDataDictionary(viewData);
}

return viewData == null ? new ViewDataDictionary(view.ViewData) { Model = model } : new ViewDataDictionary(viewData) { Model = model };
}

vzgromozdun Oct 18, 2010 at 1:12 PM 
Hi,

I'm trying to validate my view which are rendered by RenderTypePartial() and seems like no correct ViewData passed to partial typed views. Is it works fine for anyone?
Just did small investigation and looks like PartialRenderer.GetViewData() method just creates new ViewData instead of passing ViewData from Binder (with validation results).

So question are - 1) Is it a bug or feature? :-) 2) Where I can found example of derived type model binder with _working_ validation?

sdhebert Sep 30, 2010 at 3:50 PM 
My apologies - my email notifications were turned off.

@paul07481 - The need to derived from the FluentHtml.ModelViewUserControl<T> on the page is required.

It sounds like you are describing a naming collision, but I'm not entirely clear. Feel free to send sample source to steve (dot) hebert (at) gmail.com. I'd be happy to take a look.

@dmorgan - Paul's comment around the inherets attribute is spot on.

I will put together a sample project showing this working from beginning-to-end. I also have this working with Mvc2 EditorTemplates as well. I'll combine both examples into the project.

paul07481 Sep 18, 2010 at 12:13 PM 
This looks great, but there are some things to add which may help some of those people struggling to get this working. Firstly make sure the view inherits from ModelViewPage so have something like this at the top of the view:
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="MvcContrib.FluentHtml.ModelViewPage<YourModelWithDerivedClassesHere>" %>
Now the Partials are controls so make sure they inherit from ModelViewUserControl so this goes at the top of those pages:
<%@ Control Language="C#" Inherits="MvcContrib.FluentHtml.ModelViewUserControl<YourConcreteDerivedClass>" %>
The next gotcha appears to be that the PartialNameConventionService actually returns a string as {TypeName}TypePartial NOT {TypeName}TypedPartial as it says above, so either name your controls accordingly or change the ConventionService string as the author suggests above.
This is as far as I have got trying to get this to work because I am using POCO proxy objects so the TypeName cannot match the control names found using this ConventionService - looks like I will need to modify the source to accomplish what I want. Anyone has a good workaround for this please let me know!

dmorgan Aug 18, 2010 at 10:43 PM 
I couldn't replicate the given example. RenderTypedPartial doesn't exists in MvcContrib.FluentHtml

TinoW Jul 27, 2010 at 8:38 AM 
@dst:
I am struggling to get this working also. The only method called "RenderTypedPartial" that I can find is located in MvcContrib.FluentHtml.ViewModelContainerExtensions.

@sdherbert:
Your article got my attention right away as I am facing the issue that the MVC framework is trying to instantiate an abstract class instead of one of its children. It sure looks like the MvcContrib project could help me with this one but I am having difficulties in getting this to work.
It appears that the signature is inconsitent with the one used in your article. After adding MvcContrib and MvcContrib.FluentHtml as references to my MVC project I can only find one method called "RenderTypedPartial" and this one has a signature like this:

MvcContrib.FluentHtml.ViewModelContainerExtensions.RenderTypedPartial<T, TPartialViewModel>(this MvcContrib.FluentHtml.IViewModelContainer<T>, System.Linq.Expressions.Expression<System.Func<T,TPartialViewModel>>)

Some more documentation (or a link to it) would be great.

MedicineMan Jun 30, 2010 at 5:22 AM 
Hi, I'm not very familiar with MvcContrib, so your documentation left out some details that seem to be critical in getting this to work.
What imports and references do I need?

What kind of page does this inherit from? I tried: Inherits="System.Web.Mvc.ViewPage<MvcContribProject.Models.DynamicItem>"

The following syntax isn't recognized; I assume it has to do with one of the above errors: this.Label(m => m.Diameter)

I can't seem to figure out how to get RenderTypedPartial recognized. This is one of the most important steps!

otherwise, it looks like a very promising project!

dst Apr 6, 2010 at 3:39 PM 
Exactly what I was looking for. Is it possible to add a sample VS-Project that uses the derived type model binder?