1

I have a problem with ASP.Net that is causing me to tear my hair out. I can get parts of the model to bind using different approaches, but no one approach can bind all the data.

Controller Action signature

// Post: Profile/{customerId}/SetKnockOutQuestions/{profileId}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SetKnockOutQuestions(Int32 customerId,
    Int32 profileId,
    Int32 parentProfileId,
    IEnumerable<ProfileKnockOutQuestionModel> questions)
{

The relevant data members of the ProfileKnockOutQuestionModel class

public sealed class ProfileKnockOutQuestionModel {

    /// <summary>
    /// Parameterless constructor is required for auto-passing GET/POST data to controller actions.
    /// </summary>
    public ProfileKnockOutQuestionModel() { }


    [Required]
    [DisplayName("Lead Type Id")]
    public Int32 AdminLeadType { get; set; }

    //[Required]
    //[DisplayName("Question Type")]
    [UIHint("GridForeignKey")]
    public Int16 QuestionTypeId { get; set; }

    [Required]
    [DisplayName("Question Text")]
    public String QuestionText { get; set; }

    [Required]
    [DisplayName("Pass Answer")]
    public String Answer1Text { get; set; }

    [Required]
    [DisplayName("Fail Answer")]
    public String Answer2Text { get; set; }

    [Required]
    [DisplayName("Price")]
    [DataType(DataType.Currency)]
    [Range(0, Int16.MaxValue)]
    public Decimal Price { get; set; }

    public Int32 ProfileKnockOutQuestionId { get; private set; }

    public Int32 ProfileId { get; private set; }

    public Int32 ParentProfileId { get; private set; }

    public Int32 CustomerId { get; private set; }

    public Int32 AdminKnockOutQuestionId { get; private set; }

Attempting to send data in JSON encoded, javascript code

$.ajax({
    url: "/mvc/Profiles/78219/SetKnockOutQuestions/1605111",
    data: JSON.stringify({ questions: data,
        parentProfileId : 1605105 
    }),
    type: "POST",
    contentType: "application/json; charset=utf-8"
});

Sends this data (trimmed for brevity)

{
"questions":[
   {
     "AdminKnockOutQuestionId":8,
     "AdminLeadType":4,
     "Answer1Text":"Ya really!",
     "Answer2Text":"no wai",
     "CustomerId":78219,
     "ParentProfileId":1605105,
     "Price":2,
     "ProfileId":1605111,
     "ProfileKnockOutQuestionId":0,
     "QuestionText":"Really? Auto",
     "QuestionTypeId":0
   },

But once we're in the action, the model binder has only bound the strings, the price and AdminLeadType. The rest of the data is all zeros. I tried adding the [Required] and [DisplayName("")] attributes to the rest of the numeric fields after noticing that all the correctly parsed fields had them and nothing changed.

The other way I have tried to send it in is as a regular form encoded post.

AXAJ javscript

$.ajax({
    url: "/mvc/Profiles/78219/SetKnockOutQuestions/1605111",
    data: { questions: data,
        parentProfileId : 1605105 
    },
    type: "POST",
});

Which results in a post body like the following (copy/pasted from Chrome's network inspector; I could post the un-parsed, url encoded version, but instead just trust me that the ampersands and such are all in order):

questions[0][AdminKnockOutQuestionId] = 8
questions[0][AdminLeadType] = 4
questions[0][Answer1Text] = Ya really!
questions[0][Answer2Text] = no wai
questions[0][CustomerId] = 78219
questions[0][ParentProfileId] = 1605105
questions[0][Price] = 2
questions[0][ProfileId] = 1605111
questions[0][ProfileKnockOutQuestionId] = 0
questions[0][QuestionText] = Really? Auto
questions[0][QuestionTypeId] = 0

This has the opposite problem. Nothing is bound at all because the model state freaks out and says all the String Values are missing, even though I can clearly see them in the VS debugger view of the Request object.

What in the world am I doing wrong with my model binding?

2 Answers 2

1

Your properties have private setters. You cannot possibly expect the model binder to be able to bind them. So add public setters:

public sealed class ProfileKnockOutQuestionModel 
{
    /// <summary>
    /// Parameterless constructor is required for auto-passing GET/POST data to controller actions.
    /// </summary>
    public ProfileKnockOutQuestionModel() { }

    [Required]
    [DisplayName("Lead Type Id")]
    public Int32 AdminLeadType { get; set; }

    //[Required]
    //[DisplayName("Question Type")]
    [UIHint("GridForeignKey")]
    public Int16 QuestionTypeId { get; set; }

    [Required]
    [DisplayName("Question Text")]
    public String QuestionText { get; set; }

    [Required]
    [DisplayName("Pass Answer")]
    public String Answer1Text { get; set; }

    [Required]
    [DisplayName("Fail Answer")]
    public String Answer2Text { get; set; }

    [Required]
    [DisplayName("Price")]
    [DataType(DataType.Currency)]
    [Range(0, Int16.MaxValue)]
    public Decimal Price { get; set; }

    /// <summary>
    /// Public setters are required for passing GET/POST data to controller actions.
    /// </summary>
    public Int32 ProfileKnockOutQuestionId { get; set; }

    /// <summary>
    /// Public setters are required for passing GET/POST data to controller actions.
    /// </summary>
    public Int32 ProfileId { get; set; }

    /// <summary>
    /// Public setters are required for passing GET/POST data to controller actions.
    /// </summary>
    public Int32 ParentProfileId { get; set; }

    /// <summary>
    /// Public setters are required for passing GET/POST data to controller actions.
    /// </summary>
    public Int32 CustomerId { get; set; }

    /// <summary>
    /// Public setters are required for passing GET/POST data to controller actions.
    /// </summary>
    public Int32 AdminKnockOutQuestionId { get; set; }

    ...
}

Once all your properties that you want to be bound have public setters your AJAX request seems fine:

$.ajax({
    url: "/mvc/Profiles/78219/SetKnockOutQuestions/1605111",
    data: JSON.stringify({ 
        questions: data,
        parentProfileId : 1605105 
    }),
    type: "POST",
    contentType: "application/json; charset=utf-8"
});
Sign up to request clarification or add additional context in comments.

1 Comment

I am so frustrated with myself right now... Thanks for catching what I overlooked a dozen times.
0

I don't think I have ever POSTed an array but I don't know of any reason why it would not work.

I want to mention a couple things that may help:

Firstly, in your second attempt, I believe you have to use ajax's traditional parameter so asp mvc accepts it.

Secondly, I don't think you need to use JSON.stringify to post JSON so you may want to try removing that.

Lastly if all else fails make a new model like so

public class ProfileQuestions
{
    public Int32 customerId {get;set;}
    public Int32 profileId {get;set;}
    public Int32 parentProfileId {get;set;}
    public IEnumerable<ProfileKnockOutQuestionModel> questions {get;set;}
}

and change your action to

public ActionResult SetKnockOutQuestions(
    ProfileQuestions profileQs)
{

I know the last option will work for sure

3 Comments

I tried out the traditional you mentioned and it didn't change anything; the post body was the same. It looks like it won't have any effect on primitive types (non objects) or if there aren't any key collisions, neither of which apply to my data set. Also, you do have to JSON.stringify the data or jQuery will turn it back into url-form-encoded format, despite setting the content-type parameter. This is a quirk of the `type: "POST" behavior.
I may try the more complicated model you suggest tomorrow if I don't find anything else. I'd really rather keep it the way it is...
Another thing to beware is name collisions. I've been stuck a couple of times simply because 2 names collided when MVC tried to bind my data. If you dont need CustomerId, profileid, etc in the Action params try removing those.

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.