Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC Post list becomes null under very strange circumstances

So I have a controller like this:

public class TestController : Controller
    {
        //
        // GET: /Test/

        public ActionResult Index()
        {
            return View("Test");
        }

        public ActionResult Post(IList<Test> LanguageStrings, IList<Test> LanguageStringsGroup, IList<string> Deleted, IList<string> DeletedGroup)
        {
            if (LanguageStrings == null)
            {
                throw new ApplicationException("NULL");
            }


            return View("Test");
        }

    }

    public class Test
    {
        public string Val { get; set; }
        public string Another { get; set; }
    }

And a view like this:

<h2>Test</h2>

@using (Html.BeginForm("Post", "Test"))
{
    @Html.Hidden("LanguageStrings[0].Val", "test1")
    @Html.Hidden("LanguageStrings[0].Another")
    @Html.Hidden("LanguageStrings[1].Val", "test2")
    @Html.Hidden("LanguageStrings[1].Another")

    @Html.Hidden("LanguageStringsGroup[0].Val", "test4")

    @Html.Hidden("Deleted[0]")
    @Html.Hidden("Deleted[1]")
    @Html.Hidden("Deleted[2]")

    @Html.Hidden("DeletedGroup[0]")

    <button>Post</button>
}

When I post the form my controller throws the exception because LanguageStrings is null. The strange part I mentioned in the title is that if I add one more record to the list everything works. Like this:

<h2>Test</h2>

@using (Html.BeginForm("Post", "Test"))
{
    @Html.Hidden("LanguageStrings[0].Val", "test1")
    @Html.Hidden("LanguageStrings[0].Another")
    @Html.Hidden("LanguageStrings[1].Val", "test2")
    @Html.Hidden("LanguageStrings[1].Another")
    @Html.Hidden("LanguageStrings[2].Val", "test3")
    @Html.Hidden("LanguageStrings[2].Another")

    @Html.Hidden("LanguageStringsGroup[0].Val", "test4")

    @Html.Hidden("Deleted[0]")
    @Html.Hidden("Deleted[1]")
    @Html.Hidden("Deleted[2]")

    @Html.Hidden("DeletedGroup[0]")

    <button>Post</button>
}

It also works when I remove the "Deleted" list. Like this:

<h2>Test</h2>

@using (Html.BeginForm("Post", "Test"))
{
    @Html.Hidden("LanguageStrings[0].Val", "test1")
    @Html.Hidden("LanguageStrings[0].Another")
    @Html.Hidden("LanguageStrings[1].Val", "test2")
    @Html.Hidden("LanguageStrings[1].Another")

    @Html.Hidden("LanguageStringsGroup[0].Val", "test4")

    @Html.Hidden("DeletedGroup[0]")

    <button>Post</button>
}

This has something to do with the naming I am using. I have already solved the problem with renaming LanguageStrings to something else. But I would like to understand what is happening here because probably I could learn something from it how MVC maps request body and will be able to avoid similar time consuming problems. Please help me and explain the cause of this.

like image 517
John Avatar asked Nov 21 '13 13:11

John


People also ask

How to handle null values in ASP net MVC?

Type = model. Type. Trim(); whenever null values coming from the database occurs.

What is error Cshtml?

cshtml as the view not found in Index but in the web. config file. The error statusCode="404" means that the file is not found and it means the controller name or controller action method name is the issue.

What is IsPostBack in ASP NET MVC?

A PostBack in ASP.NET Web Forms is an HTTP Post. IsPostBack is a standard pattern in Web Forms to determine if the page request is a GET or a POST. This pattern does not apply to MVC. In MVC, the initialization code goes in an [HttpGet] action and the form processing code goes in an [HttpPost] action.


1 Answers

You found a bug in the PrefixContainer of MVC 4 which has already been fixed in MVC 5.

Here is the fixed version with comments about the bug:

internal bool ContainsPrefix(string prefix)
{
    if (prefix == null)
    {
        throw new ArgumentNullException("prefix");
    }

    if (prefix.Length == 0)
    {
        return _sortedValues.Length > 0; // only match empty string when we have some value
    }

    PrefixComparer prefixComparer = new PrefixComparer(prefix);
    bool containsPrefix = Array.BinarySearch(_sortedValues, prefix, prefixComparer) > -1;
    if (!containsPrefix)
    {
        // If there's something in the search boundary that starts with the same name
        // as the collection prefix that we're trying to find, the binary search would actually fail.
        // For example, let's say we have foo.a, foo.bE and foo.b[0]. Calling Array.BinarySearch
        // will fail to find foo.b because it will land on foo.bE, then look at foo.a and finally
        // failing to find the prefix which is actually present in the container (foo.b[0]).
        // Here we're doing another pass looking specifically for collection prefix.
        containsPrefix = Array.BinarySearch(_sortedValues, prefix + "[", prefixComparer) > -1;
    }
    return containsPrefix;
}
like image 143
LostInComputer Avatar answered Oct 09 '22 09:10

LostInComputer