Friday, November 29, 2013

Public field not bound when posting to WebAPI (or a deep dive into WebAPI model binding)

When trying to create a sandbox project using WebAPI (on MVC4), I was struggling with a weird problem: My data wasn't being received in the server. I had the following jQuery call
$.post("api/Values", {value1:1, value2:2}, function(result){ console.log(result); })
and the WebAPI service action that I was targeting was something like this
public IEnumerable Post(Dummy value)
{
 return new string[] { value.Value1, value.Value2 };
}
I noticed that even that the instance of Dummy was being created, Value1 and Value2 where always null. The Dummy class was
public class Dummy
{
 public string Value1;
 public string Value2;
}
Pretty simple, right?. Well, after reading doing a lot of research, I changed by accident one of the Dummy fields to become a property
public class Dummy
{
 public string Value1;
 public string Value2 {get;set;}
}
I tested again and VoilĂ !!... well, half voilĂ  actually... When posting, now I was receiving data in Value2, but still not in Value1. This was really intriguing... how come property was being assigned correctly but not the field? Both are public, right? Why the difference?
Obviously, I knew the solution was changing both fields to be properties now, but I wanted to know why was that happening. I started digging on how WebAPI works and found a really interesting Web API poster, that describes the full lifecycle of a HTTP message. There I got my first clue, so I started researching on how ModelBinding happens. As described there, one of the binding methods is MediaTypeFormatter. Since I was sending JSON object, I tested the Deserialization process based on the test methods provided in the WebAPI overview site
T Deserialize(MediaTypeFormatter formatter, string str) where T : class
{
 // Write the serialized string to a memory stream.
 Stream stream = new MemoryStream();
 StreamWriter writer = new StreamWriter(stream);
 writer.Write(str);
 writer.Flush();
 stream.Position = 0;
 // Deserialize to an object of type T
 return formatter.ReadFromStreamAsync(typeof(T), stream, null, null).Result as T;
}
passing the same JSON object that I had on my jQuery call. The result: The method assigned successfully the values for both the field and the property. By inspecting the HTTP Request headers, I found out that data wasn't being actually sent as JSON but in the following format: Content-Type:application/x-www-form-urlencoded; charset=UTF-8, which tells the server that data is being sent like this: Value1=1&Value2=2. Then, we need to change the AJAX call to be like this
$.ajax({
  url: "api/Values",
  data: JSON.stringify({Value1:1,Value2:2}),
  type: "POST",
  contentType:"application/json; charset=utf-8"
})
please notice 2 things: I changed the contentType for the request AND Stringified the JSON object. By doing these changes, Dummy public fields were now populated correctly.
Now, I still wanted to know why my values weren't bound when I wasn't specifying the request content type. Doing more research, I found this really interesting article by Mike Stall called How WebAPI does parameter binding which states
There are 2 techniques for binding parameters: Model Binding and Formatters. In practice, WebAPI uses model binding to read from the query string and Formatters to read from the body
If you are not yet bored, you might remember that when we didn't specify the request content type, the data was being sent as Content-Type:application/x-www-form-urlencoded; charset=UTF-8. This means, that WebAPI was using ModelBinding (and not formatters) to populate the Dummy instance. Moreover, the article has another interesting declaration:
ModelBinding is the same concept as in MVC, [...]. Basically, there are “ValueProviders” which supply pieces of data such as query string parameters, and then a model binder assembles those pieces into an object.
And how does ModelBinding work in MVC? That was my next question. And I was really happy that Microsoft open-sourced the ASP.Net WebStack, because there is where we can find the answer. If we look into DefaultModelBinder source code, we'll find that when talking about complex models, it only looks for the object properties to populate the data (maybe because having public fields is a bad practice).
Well, I hope you can find this post as interesting as I found learning all this. Some times making silly errors can drive you into learn really interesting things.

Useful references


Tuesday, November 19, 2013

Backing field for automatically implemented property [Field] must be fully assigned before control is returned to the caller

Working with structs in C# gives you a lot of flexibility on the way you design your applications, but since they are not reference types, they have some special features that we need to take in count.
Recently I was working on a web application and I created a struct to hold a pair of values that is being used very frequently. It is something like this
public struct StringTuple{
    public string Value1 {get; set;}
    public string Value2 {get; set;}
}
After some code changes, I decided that it would be a good option to have a constructor to pass the struct values
public struct StringTuple
{
 public StringTuple(string value1, string value2)
 {
  Value1 = value1;
  Value2 = value2;
 }
 public string Value1 { get; set; }
 public string Value2 { get; set; }
}
but the compiler started complaining, giving me the following error
Backing field for automatically implemented property Value1 must be fully assigned before control is returned to the caller
It was the first time that I had seen that error, so after some time of think and research I remembered one of the basic principles of working with structs: members are initialized when the default constructor is called. That is why creating a new constructor was creating a problem, since we were overloading the constructor call and skipping that member initialization

The solution

Since the problem is that we're not calling the default constructor, the solution is obviously call it, so we just need to add that call to the constructor that we just introduced.
public struct StringTuple
{
 public StringTuple(string value1, string value2):this()
 {
  Value1 = value1;
  Value2 = value2;
 }
 public string Value1 { get; set; }
 public string Value2 { get; set; }
}
By that, the error message is gone and we can continue happily working with structs

 

Copyright @ 2013 A learning journey.