I have two nearly identical scenarios regarding model binding on a controller action. One works, and one doesn't. I can't figure out why.
This works:
Given this ViewModel class:
Public Class SeasonCreateViewModel
Public Property Season As Season
End Class
We have these actions
Function Create() As ActionResult
Dim seasonVM As New SeasonCreateViewModel()
Return View("Create", seasonVM)
End Function
<HttpPost()>
<ValidateAntiForgeryToken()>
Function Create(seasonVM As SeasonCreateViewModel) As ActionResult
End Function
And everything binds perfectly. seasonVM.Season contains the values posted from the form.
HOWEVER, this doesn't work:
Given this ViewModel class:
Public Class UserCreateViewModel
Public UserPerson As UserPersonModel
End Class
And these actions:
Function Create() As ActionResult
Dim userVM As New UserCreateViewModel()
Return View("Create", userVM)
End Function
'
' POST: /Admin/User/Create
<HttpPost()>
<ValidateAntiForgeryToken()>
Function Create(userVM As UserCreateViewModel) As ActionResult
End Function
userVM.UserPerson does not bind to the form values the same way seasonVM.Season does. In fact, it is Nothing (aka. null)
Does anyone have any ideas?
If you're curious about the views, they are structured identically, as in:
@Using Html.BeginForm()
@Html.AntiForgeryToken()
@Html.ValidationSummary(True)
<div class="editor-label">
@Html.LabelFor(Function(model) model.UserPerson.NewUsername)
</div>
<div class="editor-field">
@Html.EditorFor(Function(model) model.UserPerson.NewUsername)
@Html.ValidationMessageFor(Function(model) model.UserPerson.NewUsername)
</div>
<p>
<input type="submit" value="Create" />
</p>
End Using
AND
@Using Html.BeginForm()
@Html.AntiForgeryToken()
@Html.ValidationSummary(True)
<div class="editor-label">
@Html.LabelFor(Function(model) model.Season.SeasonDescription)
</div>
<div class="editor-field">
@Html.EditorFor(Function(model) model.Season.SeasonDescription)
@Html.ValidationMessageFor(Function(model) model.Season.SeasonDescription)
</div>
<p>
<input type="submit" value="Create" />
</p>
End Using
Just a note: I've omitted irrelevant code, mostly just additional properties on the view pages. I will say there is no property named "userVM" on my UserPersonModel as was the case here: Model is null when form submitted
UPDATE
OK. I think I'm about ready to give up on figuring out why Season is binding properly, but UserPerson is not.
I thought I had figured out the answer, but it didn't seem to actually make a difference:
I have
Public Class SeasonCreateViewModel
Public Property Season As Season
End Class
and I have
Public Class UserCreateViewModel
Public UserPerson As UserPersonModel
End Class
When lined up like this, the difference seems obvious. In SeasonCreateViewModel, I have a property Season identically named to the class it is an instance of (Season). In UserCreateViewModel, I have a property UserPerson, which is named slightly differently from its class UserPersonModel. Because of this, I thought the model binder does not automatically match userVM.UserPerson to its corresponding class.
So I changed the class UserPersonModel to UserPerson so the Form values would match up in the same way they do for Season (ie, to the classname), but it STILL did not fix it.
What does fix it, however, is if I change this:
Function Create(userVM As UserCreateViewModel) As ActionResult
to this
Function Create(userPerson As UserPerson) As ActionResult
Why this suddenly binds properly, where it didn't before? I have no idea. Does this help anyone answer this question, though?