简体   繁体   中英

ASP.NET MVC3 model binder data is invalid when a getter is used against the collection

With this Contact model

public class Contact
{
    public string Name { get; set; }
    public ICollection<Phone> Phones { get; set; }

    public Phone PrimaryPhone
    {
        get { return Phones.FirstOrDefault(x => x.Primary) ?? new Phone(); }
    }
}

public class Phone
{
    public bool Primary { get; set; }
    public string PhoneNumber { get; set; }
    public string Type { get; set; }
}

And this controller

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    [HttpPost]
    public ActionResult Index(Contact contact)
    {
        return View();
    }
}

When I POST to the HomeController Index using jQuery

(function ($) {
        var myData = {
            Name: 'Wesley Crusher',
            Phones: [
                { Primary: false, PhoneNumber: '111-111-1111', Type: 'Business' },
                { Primary: true,  PhoneNumber: '222-222-2222', Type: 'Personal' },
                { Primary: false, PhoneNumber: '333-333-3333', Type: 'Business' }
            ],
            PrimaryPhone: { Primary: true, PhoneNumber: '111-111-1111', Type: 'Business' }
        };

        $.ajax({
            url: '@Url.Action("Index", "Home")',
            type: 'POST',
            contentType: 'application/json',
            data: JSON.stringify(myData)
        });
    })(jQuery)

The model binder incorrect builds up the ICollection Phones. The data is:

  • [0] Primary=false, PhoneNumber="111-111-1111", Type="Business" MVC3ModelBinderJsonTesting.Models.Phone
  • [1] Primary=true, PhoneNumber="111-111-1111", Type="Business" MVC3ModelBinderJsonTesting.Models.Phone
  • [2] Primary=false, PhoneNumber="333-333-3333", Type="Business" MVC3ModelBinderJsonTesting.Models.Phone

The PhoneNumber "111-111-1111" is repeated and the Type is "Business" instead of "Personal". Is this expected behavior for some reason or is this a bug?

I can post a sample project if you'd like, let me know.

I reckon posting the PrimaryPhone is causing the problem. Try removing

PrimaryPhone: { Primary: true, PhoneNumber: '111-111-1111', Type: 'Business' }

As this property has only a getter, and would correctly be determined by the Primary property, it should still have valid data.

I think it's because it's not a primitive. It's a complex object so the model binder tries to set its properties.

Model binding is better suited to binding against "input models" which represent the input coming in from the form. Having calculated properties with business logic on the input model might not be the best approach as you saw.

You could probably have that be an extension method (sadly no support for extension properties) rather than a property of your input model. Or even a proper method. Having it be a property makes the model binder think it's fair game.

If it was a get only primitive type, it wouldn't try to set it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM