views:

31

answers:

1

My classes are a DataModel, which uses ID as its PK, and a Map, which also has ID as a PK. Map has a FK to DataModel (DataModelID).

I have a partial view that I use from my DataModel list view as follows:

 <% foreach (var map in Model.Map)
    { %>
 <% Html.RenderPartial("MapEdit", map); %>
 <% } %>

My MapEdit partial view looks like:

Before: <%= Html.Encode(Model.ID) %>

<% using (Html.BeginForm()) {%>
    <label for="ID">ID:</label>
    <%= Html.TextBox("ID", Model.ID) %>
    <%= Html.ValidationMessage("ID", "*") %>
<% } %>

When I run this, I would expect to see ([ ] represent a textbox):

Before: 4
ID: [ 4 ]

Before: 5
ID: [ 5 ]

etc...

Where each number is the actual ID from the record. What I actually get is:

Before: 4
ID: [ 10 ]

Before: 5
ID: [ 10 ]

10 in this case happens to be the ID from the DataModel! I have no idea where this is coming from, because stepping thru the code shows that Model.ID has the correct value - at some point however it's getting replaced.

Can anyone explain this?

+2  A: 

If Model.Map is null, Model will be passed to the partial view instead. That's how partial views work.

Also, model values are copied to the ModelStateDictionary, which can have only one key called ID. This makes sense since any form you submit would use the keys as the form field name.

ModelStateDictionary values supercede model values so that the form can be re-rendered with invalid user input when necessary. Usually, model and model state values are the same on initial (non-error) display. But this isn't the same if you have duplicate keys.

Craig Stuntz
And the solution is using prefixes and list binding (http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx).
LukLed
+1, concise. There's more explanation on this thread as well: http://stackoverflow.com/questions/953330/does-html-textbox-uses-request-params-instead-of-model
womp
Model is not null. The 3 and 4 are the right IDs, and stepping thru the code the Model.ID is getting passed to the html.TextBox with the correct value - but at some point it gets replaced.
chris
LukLed, that post was accurate at the time, but list binding has changed since it was written. The index is (1) no longer a separate field (2) required to be an integer (3) must start at zero and (4) must be sequential.
Craig Stuntz
@chris: Don't use the same names ("ID") in view and partial views. it doesn't work well with ModelState. Use prefixes: Html.TextBox("maps[index].ID", Model.ID).
LukLed
@Craig Stuntz: I know, but it gives good overview and I knew that someone will add comment about index:)
LukLed
@LukLed: That worked. If you want to write it up as an answer, I'll accept it.
chris
@chris: Craig Stuntz actually answered your question. It is good to know how ModelState works, it will save you some time:)
LukLed