Correct Model Binding for Derived Types in ASP.NET MVC 6

I ran into another fun problem today (why do so many of my blog posts contain that phrase?): I'm working on an ASP.NET 5 (MVC 6 / DNX) application, and I'd created a class that derived from another class.

For example:

(pseudo-code)
public class Element
public class HeaderElement : Element

There's also a class which contains a generic list of the parent type:

(pseudo-code)
public class Section 
{
...
(other stuff)
...
   public List Elements { get; set; }
}

Nothing unusual here, right? Well, I was sending that Section class shown above to client-side calls in JSON format, and everything was fine. However, when POSTing or PUTing back to my server-side controller, all instances of that derived HeaderElement class were instead deserialized as instances of plain old Element (the base class). It looked like I was going to have to write my own custom model binder for this...

...or so I thought. Turns out, there was a much simpler solution: setting the TypeNameHandling property on the input JSON serializer to Auto. Check it out:

        // This method gets called by a runtime.
        // Use this method to add services to the container
        public void ConfigureServices(IServiceCollection services)
        {
       services.RegisterIoCComponents();

            services.AddMvc().Configure(options =>
            {
                options.OutputFormatters
                    .Where(f => f is JsonOutputFormatter)
                    .Cast()
                    .First()
                    .SerializerSettings.ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();

                options.InputFormatters
                    .Where(f => f is JsonInputFormatter)
                    .Cast()
                    .First()
                    .SerializerSettings.TypeNameHandling = TypeNameHandling.Auto;
            });            

            services.Configure(_config.GetConfigurationSection("AppSettings"));     
        }

This works by adding a $type property to the JSON that is served to the client, and then using that to correctly deserialize the object when sent back to the server. It didn't even require any modification to the output formatter -- it just handled it automatically.

A simple solution to something that could've been a little more work.

Comments

Popular posts from this blog

A Generic Method Using HttpClient to Make a Synchronous Get Request

The Cause and Solution for the "System.Runtime.Serialization.InvalidDataContractException: Type 'System.Threading.Tasks.Task`1[YourTypeHere]' cannot be serialized." Exception

How To Mock Out Child Components In Unit Tests of Angular 2 Code