I've been trying to read about this DefaultModelBinder for a couple of days now but I'm still very confused. I am using MVC 4 & EF 5 TablePerHiearchy structure.
My problem is that I have a base class of Resource:
public class Resource : PocoBaseModel
{
private int _resourceID;
private string _title;
private string _description;
//public accessors
}
that has sub classes (DVD, EBook, Book, etc)
public class DVD : Resource
{
private string _actors;
//more fields and public accessors
}
My controller code uses a custom ModelBinder
[HttpPost]
public ActionResult Create([ModelBinder(typeof(ResourceModelBinder))] Resource resource)
{
//controller code
}
public class ResourceModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
var type = controllerContext.HttpContext.Request.Form["DiscriminatorValue"];
bindingContext.ModelName = type;
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, resourceTypeMap[type]);
return base.BindModel(controllerContext, bindingContext);
}
static Dictionary<string, Type> resourceTypeMap = new Dictionary<string, Type>
{
{"Resource", typeof(Resource)},
{"Book", typeof(Book)},
{"DVD", typeof(DVD)},
{"EBook", typeof(EBook)},
{"Hardware", typeof(Hardware)},
{"Software", typeof(Software)}
};
}
So that I could pass my view a Resource (casted as a DVD, Book, or any other type)
@model Models.Resource
@{
ViewBag.Title = "Create";
}
<h2>Create</h2>
@using (Html.BeginForm("Create", "Admin", null, FormMethod.Post, null))
{
@Html.ValidationSummary(true)
<fieldset>
<legend>Resource</legend>
@Html.HiddenFor(model => model.ResourceID)
@Html.HiddenFor(model => model.ResourceTypeID)
@Html.HiddenFor(model => model.Committed)
@Html.Partial("_CreateOrEdit", Model)
<p>
<input type="submit" value="Create"/>
</p>
</fieldset>
}
and bind it based on its derived properties which happens in a switch inside the partialview.
@using Models.ViewModels;
@using Models.ResourceTypes;
@using Helper;
@model Models.Resource
@Html.HiddenFor(model => Model.DiscriminatorValue);
<table cellspacing="2" cellpadding="2" border="0">
@{
string type = Model.DiscriminatorValue;
switch (type)
{
case "Book":
Book book = (Book)Model;
<tr>
<td colspan="2">
<div class="editor-label" style="padding-top: 15px;">
@Html.LabelFor(model => model.Title)
</div>
<div class="editor-field">
@Html.TextAreaFor(model => model.Title, new { style = "width: 750px; height: 65px;" })
@Html.ValidationMessageFor(model => model.Title)
</div>
</td>
</tr>
<tr>
<td>
<div class="editor-label">
@Html.LabelFor(model => book.Edition)
</div>
<div class="editor-field">
@Html.TextBoxFor(model => book.Edition, new { style = "width: 150px;" })
@Html.ValidationMessageFor(model => book.Edition)
</div>
</td>
<td>
<div class="editor-label">
@Html.LabelFor(model => book.Author)
</div>
<div class="editor-field">
@Html.EditorFor(model => book.Author)
@Html.ValidationMessageFor(model => book.Author)
</div>
</td>
</tr>
<tr>
<td>
<div class="editor-label">
@Html.LabelFor(model => book.Pages)
</div>
<div class="editor-field">
@Html.TextBoxFor(model => book.Pages, new { style = "width: 75px;" })
@Html.ValidationMessageFor(model => book.Pages)
</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="editor-label">
@Html.LabelFor(model => model.Description)
</div>
<div class="editor-field">
@Html.TextAreaFor(model => model.Description, new { style = "width: 750px; height: 105px;" })
@Html.ValidationMessageFor(model => model.Description)
</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="editor-label">
@Html.LabelFor(model => model.AdminNote)
</div>
<div class="editor-field">
@Html.TextAreaFor(model => model.AdminNote, new { style = "width: 750px; height: 105px;" })
@Html.ValidationMessageFor(model => model.AdminNote)
</div>
</td>
</tr>
<tr>
<td>
<div class="editor-label">
@{ int copies = Model == null ? 1 : Model.Copies; }
@Html.LabelFor(model => model.Copies)
</div>
<div class="editor-field">
@Html.TextBoxFor(model => model.Copies, new { style = "width: 75px;", @Value = copies.ToString() })
@Html.ValidationMessageFor(model => model.Copies)
</div>
</td>
</tr>
<tr>
<td>
<div class="editor-label">
@Html.LabelFor(model => book.ISBN10)
</div>
<div class="editor-field">
@Html.EditorFor(model => book.ISBN10)
@Html.ValidationMessageFor(model => book.ISBN10)
</div>
</td>
<td>
<div class="editor-label">
@Html.LabelFor(model => book.ISBN13)
</div>
<div class="editor-field">
@Html.EditorFor(model => book.ISBN13)
@Html.ValidationMessageFor(model => book.ISBN13)
</div>
</td>
</tr>
break;
My first problem was that when I posted the form back, it went back as a resource and not as the casted type (so I was losing all the derived type properties) which is why I created the ResourceModelBinder. Now it correctly binds/postsback the casted type but it does not bind the base class properties of resource like Title, ResourceID, ResourceTypeID..
Can anyone help me understand what I am missing so that it actually binds the base resource class properties as well as the derived type properties.?