0

The issue is very similar to this post How to implement custom JsonConverter in JSON.NET to deserialize a List of base class objects? However instead of trying to serialize a string manually we are attempting to use the model binding in MVC 3. So here is the scenario

[DataContract]
public class Company
{

    [DataMember]
    public List<Person> Employees { get; set; }
}

 [DataContract]
public class Person
{
     [DataMember]
     public string FirstName { get; set; }
     [DataMember]
     public string LastName { get; set; }
}

[DataContract]
[KnownType(typeof(Person))]
public class Employee : Person
{
    [DataMember]
    public string Department { get; set; }
    [DataMember]
    public string JobTitle { get; set; }
}

[DataContract]
[KnownType(typeof(Person))]
public class Artist : Person
{
    [DataMember]
    public string Skill { get; set; }
}

public JsonResult PopulateCompany()
{

    Company model = new Company();
    model.Employees = new List<Person>
    {
        new Employee(),
        new Employee(),
        new Artist(),
    };

    return Json(model, JsonRequestBehavior.AllowGet);
    // in the View the model is correctly deserialized. E.g. we can see the properties from Artist


}

public ActionResult PopulateCompany(Company model)
{
    // the returned model is also being populated except the Person object is being added to the Employees and we can no longer access the properties of Artist.
    return View(model);
}

Thank you.

Community
  • 1
  • 1

1 Answers1

2

The model binding process involve first initializing the model. In your case it initializes an instance of Company with a property List<Person> Employees. Based on the values that are posted back, if a key/value pair is found that matches a Person (e.g. Persons[0].FirstName: "Ian") then a new instance of Person is initialized and its properties are set and added to the collection.

The DefaultModelBinder has no way of knowing that you want to initialize a different concrete type.

The easy solution is to use a view model containing collection properties of each type (e.g. public List<Employees> Employees { get; set; }; public List<Artist> Artists { get; set; }; etc).

The alternative (difficult) solution is to create a custom ModelBinder that will generate concrete types based on values in the model. This article (the section on Abstract Model Binder) is a good start for learning how to create a custom ModelBinder

  • You are exactly correct. The solution was to create a custom model binder and override BindModel and CreateModel. In my case detected which model to use based on controllerContext. Thank you for your assistance. – Ian Benoliel Apr 16 '15 at 23:05