4

So I was playing around with my ASP.NET MVC 4 solution. Every thing worked fine kept adding things but something odd started happening.

One of my Models properties was null, even though I had items in the Json passed to it.

This was the javascript object/json passed it it:

var obj = {
    "plc": "False",
    "al": ["386", "710"],
    "pl": ["9530", "211", "783"]
};

I was using a Custom Model binder ... thought that might be the issue so I turned it off.

Tried using the JavaScriptSerializer from .NET to see it that worked:

var reader = new StreamReader(Request.InputStream);
Request.InputStream.Position = 0;
var readToEnd = reader.ReadToEnd();

var javaScript = new JavaScriptSerializer();
var searchFarmOptions = javaScript.Deserialize<Test>(readToEnd);

Got all the properties set ... WOOT.

So I tried a clean ASP.NET MVC 4 solution. To reproduce the bug.

This is from the Index.cshtml view

@{
    ViewBag.Title = "title";
}

<h1>Title</h1>
Testing ... 
<script src="/Scripts/jquery-1.8.2.min.js" type="text/javascript"></script>
<script>
$(function() {
    var obj = {
        "1pllort": "False",
        "1plc": "true",
        "al": ["386", "710"],
        "pl": ["9530", "211", "783"]
    };

    var options = {
        "contentType": "application/json; charset=UTF-8",
         "type": "POST",
         "data" : JSON.stringify(obj)
    };
    $.ajax("/Home/TestPost", options).done(function(data) {
        console.log(data);
    });
});
</script>

This is my HomeController

using System.Collections.Generic;
using System.Web.Mvc;

namespace MvcApplication3.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View("Index");
        }

        [HttpPost]
        public ActionResult TestPost(Test model)
        {
            return Json(model);
        }
    }

    public class Test
    {
        public List<int> PL { get; set; }
        public List<int> AL { get; set; }
        public bool PLC { get; set; }
        public bool ALC { get; set; }
    }
}

Yes, the bug is still there.

Whenever I have a property starting with "pl" as my list name is .. the "pl" list is null.

Also, it could be any name starting with "pl" ... like "plchecked"

If I rename the "plc" to "cpl" its working.

So what is going on here ... is there any naming restrictions in the model binder? What am I missing here?

Update 1

Work

PL server side now have the right values, etc. not null, but the list of numbers.

var obj = {
    "pl": ["9530", "211", "783"],
    "1plc": "false",
    "pl-some-odd-value": "false",
    "al": ["386", "710"],
    "alc": "false"
};

Don't work

PL server side now have null value.

var obj = {
    "pl": ["9530", "211", "783"],
    "al": ["386", "710"],
    "alc": "false",
    "pl-odd-value": "false"
};

Work

PL has the 3 values som the json object string ...

var obj = {
    "pl": ["9530", "211", "783"],
    "al": ["386", "710"],
    "alc": "false",
    "odd-value-pl": "false"
};
5
  • Do u mean AL and ALC values are not null? Commented Feb 27, 2013 at 15:31
  • AL and ALC get the right values ... when PLC is set PL is always null. Commented Feb 27, 2013 at 15:37
  • See Update 1 ... I'm just wondering if the ModelBinder does some magic with some property names. Commented Feb 27, 2013 at 15:51
  • I've never experienced anything like this myself, by it's very nature, the model binder must accept any valid object name, because your model can include any valid object name. However, the best place to post things like this is the official CodePlex site for MVC: aspnet.codeplex.com/workitem/list/basic Commented Feb 27, 2013 at 17:40
  • I just posted here to confirm there is something silly going on. Even the TryUpdateModel method within a ActionMethod is working. I'm all out of words and the ModelBinder stuff is a bit complex for me. Commented Feb 27, 2013 at 18:07

3 Answers 3

5

Install ImranB.ModelBindingFix nuget package. I think you are hitting a bug which I have discussed in this blog post.

Sign up to request clarification or add additional context in comments.

Comments

3

There is definitely a bug. I just spent hours debugging the MVC 4 source code for the same problem. The primary root cause it that you have a collection whose name is the beginning of another property name. In your example: pl and pl-odd-value.

If you play around some more, you may find that sometimes it works properly. The reason for it seeming to be random is that the model binder performs a binary search to locate the value to bind. The binary search uses a "divide and conquer" approach to finding the property in a sorted list of the properties that were posted. If the search tests the p1-odd-value in its divide and conquer, the problem occurs. If the search skips this value, no problem.

This problem is most repeatable when you simply have the pl collection with 1 element and the pl-odd-value property in the object.

Here is a bit more info related to this. There is a sortedList of all of the values posted back. In the case of your failed test, the sorted list looks like this:

alc, "false",
al[0], "386"
al[1], "710"
pl-odd-value, "false"
pl[0], "9530"
pl[1], "211"
pl[2], "783"

Notice that the sort order uses StringComparer.OrdinalIgnoreCase, which places the square bracket items AFTER the text-only item. When MVC is performing its binary search for the al or pl collection, it is searching for al or pl, NOT al[ or pl[. By searching for the collection name without a square bracket, al < alc, not greater than alc. Problem is that we want to look after alc or pl-odd-value to get to the collection.

The workaround is to make sure that none of your property names start with the name of any of your collection property names. Rename your al collection to maybe al_collection and your pl collection to maybe pl_collection.

Comments

-1

Great answer!

I had the same problem today and I spent almost 3 hours trying to figure out what the issue was when suddenly I realized that properties with the same prefix has something weird

More info here:

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.