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

Resolving the "n timer(s) still in the queue" Error In Angular Unit Tests

Silent Renew and the "login_required" Error When Using oidc-client

How to Get Norton Security Suite Firewall to Allow Remote Desktop Connections in Windows

Fixing the "Please add a @Pipe/@Directive/@Component annotation" Error In An Angular App After Upgrading to webpack 4

How to Determine if a Column Exists in a DataReader