This can be achieved through overriding CreateModel(…). I will demonstrate that with an example.
1. Lets create a model and some base and child classes.
public class MyModel
{
public MyBaseClass BaseClass { get; set; }
}
public abstract class MyBaseClass
{
public virtual string MyName
{
get
{
return "MyBaseClass";
}
}
}
public class MyDerievedClass : MyBaseClass
{
public int MyProperty { get; set; }
public override string MyName
{
get
{
return "MyDerievedClass";
}
}
}
2. Now create a modelbinder and override CreateModel
public class MyModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
/// MyBaseClass and MyDerievedClass are hardcoded.
/// We can use reflection to read the assembly and get concrete types of any base type
if (modelType.Equals(typeof(MyBaseClass)))
{
Type instantiationType = typeof(MyDerievedClass);
var obj=Activator.CreateInstance(instantiationType);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, instantiationType);
bindingContext.ModelMetadata.Model = obj;
return obj;
}
return base.CreateModel(controllerContext, bindingContext, modelType);
}
}
3. Now in the controller create get and post action.
[HttpGet]
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
MyModel model = new MyModel();
model.BaseClass = new MyDerievedClass();
return View(model);
}
[HttpPost]
public ActionResult Index(MyModel model)
{
return View(model);
}
4. Now Set MyModelBinder as Default ModelBinder in global.asax This is done to set a default model binder for all actions, for a single action we can use ModelBinder attribute in action parameters)
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
ModelBinders.Binders.DefaultBinder = new MyModelBinder();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
5. Now we can create view of type MyModel and a partial view of type MyDerievedClass
Index.cshtml
@model MvcApplication2.Models.MyModel
@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index</h2>
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
<legend>MyModel</legend>
@Html.EditorFor(m=>m.BaseClass,"DerievedView")
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
DerievedView.cshtml
@model MvcApplication2.Models.MyDerievedClass
@Html.ValidationSummary(true)
<fieldset>
<legend>MyDerievedClass</legend>
<div class="editor-label">
@Html.LabelFor(model => model.MyProperty)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.MyProperty)
@Html.ValidationMessageFor(model => model.MyProperty)
</div>
</fieldset>
Now it will work as expected, Controller will receive an Object of type “MyDerievedClass”.
Validations will happen as expected.