I\'m trying to work out the best architecture for handling model type hierarchies within an MVC application.
Given the following hypothetical model -
Sure, but it takes a little leg work.
First, in each of your edit/create views, you need to emit the type of model you are editing.
Second, you need add a new modelbinder for the person class. Here is a sample of why I would do for that:
public class PersonModelBinder :DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
PersonType personType = GetValue<PersonType>(bindingContext, "PersonType");
Type model = Person.SelectFor(personType);
Person instance = (Person)base.CreateModel(controllerContext, bindingContext, model);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => instance, model);
return instance;
}
private T GetValue<T>(ModelBindingContext bindingContext, string key)
{
ValueProviderResult valueResult =bindingContext.ValueProvider.GetValue(key);
bindingContext.ModelState.SetModelValue(key, valueResult);
return (T)valueResult.ConvertTo(typeof(T));
}
}
Register it in your app start:
ModelBinders.Binders.Add(typeof(Person), new PersonModelBinder());
The PersonType is what I tend to use in each model and is an enum that says what each type is, I emit that in a HiddenFor so that it comes back in with the post data.
The SelectFor is a method that returns a type for the specified enum
public static Type SelectFor(PersonType type)
{
switch (type)
{
case PersonType.Student:
return typeof(Student);
case PersonType.Teacher:
return typeof(Teacher);
default:
throw new Exception();
}
}
You can now do something like this in your controller
public ActionResult Save(Person model)
{
// you have a teacher or student in here, save approriately
}
Ef is able to deal with this quite effectively with TPT style inheritance
Just to complete the example:
public enum PersonType
{
Teacher,
Student
}
public class Person
{
public PersonType PersonType {get;set;}
}
public class Teacher : Person
{
public Teacher()
{
PersonType = PersonType.Teacher;
}
}