views:

1327

answers:

3

I have modified the Nerd Dinner application to allow editing of child records by adding the following code to the DinnerForm.ascx

  <p>
    <%int i = 0;
   foreach (NerdDinner.Models.RSVP rsvp in this.Model.Dinner.RSVPs)
       { %>

        <%= Html.Hidden("Dinner.RSVPs[" + i + "].RsvpID", rsvp.RsvpID)%>
        <%= Html.Hidden("Dinner.RSVPs[" + i + "].DinnerID", rsvp.DinnerID)%>
        <%= Html.TextBox("Dinner.RSVPs[" + i + "].AttendeeName", rsvp.AttendeeName)%>
    <% i += 1;
   } %>
    </p>

it is rendering this:

<p>
    <input id="Dinner_RSVPs[0]_RsvpID" name="Dinner.RSVPs[0].RsvpID" type="hidden" value="36" />
        <input id="Dinner_RSVPs[0]_DinnerID" name="Dinner.RSVPs[0].DinnerID" type="hidden" value="63" />
        <input id="Dinner_RSVPs[0]_AttendeeName" name="Dinner.RSVPs[0].AttendeeName" type="text" value="kp" />
    <input id="Dinner_RSVPs[1]_RsvpID" name="Dinner.RSVPs[1].RsvpID" type="hidden" value="37" />
        <input id="Dinner_RSVPs[1]_DinnerID" name="Dinner.RSVPs[1].DinnerID" type="hidden" value="63" />
        <input id="Dinner_RSVPs[1]_AttendeeName" name="Dinner.RSVPs[1].AttendeeName" type="text" value="jim" />

    </p>

I have not modified the DinnersControler's Post Edit Action method. The Parent dinner is getting updated as usual, but it appears the UpdateModel(dinner); is not seeing/updating the child RSVP records.

I have tried a few variations on rendering the child records so that the Model binders will see the collection, with no luck.

Is updating parent/child records in one shot by calling UpdateModel(Parent); possible with the current model binders?

A: 

Ok, so nobody's answering. I don't know the nerddinner app but was interested by your problem. Was hoping to see some answers but, well, none yet. Not 100% sure because of lack of familiarity with ndinner, but could it be related to this post where they mention binding/updating only when a property is explicitly passed?

David Archer
+4  A: 

I haven't been able to do this myself.

I know that you can update a single child element, ie, Dinner.RSV automatically. I've never seen the ability to update a child enumerable, which would require the binding to know which property is the ID and look for it (ie, Dinner.RSVP.Where(r => r.RSVP_ID == input_id) and then update that). I don't know enough about custom binding to do something like that.

However, what I have done is to do a loop and specify the rsvp and the int as a prefix:

So you do:

UpdateModel("Dinner", Dinner);

to update the parent and then:

int i = 0;

foreach (var r in Dinner.RSVPs) {
  UpdateModel(r, "Dinner.RSVPs[" + i++ + "]");
}

Not quite as clean, but it works well for me. It might take a bit more effort to build in validation, though (you want to validate all at the same time, and make sure you don't jump back to the view on the first RSVP with an error).

EDIT: Fixed the code to reflect the OP's solution, including a bug in my parameter order. With that being said, I'm more comfortable using the RSVP.ID property than a running integer. As long as you know that Dinner.RSVPs will be the same on the POST as the GET (I'm confident of this in my code), then using the RSVP.ID will work. Should RSVPs be different, then only those present on both will get updated. However, using the sequential int could potentially cause the wrong object to be updated.

Hope that helps, James

James S
@jamesshannon This answer is close. Fix your Update Model in the loop to read int i = 0;foreach (RSVP r in Dinner.RSVPs) { UpdateModel(r, "Dinner.RSVPs[" + i + "]");i++;}and we can mark this done/answered.
KP
A: 

Your question is whether updating parent/child records in one shot by calling UpdateModel(Parent); possible with the current model binders?

Yes, this is possible. However, it is not possible (without some minor workarounds) if you are using Linq-to-Sql and your child objects are exposed via EntitySet<T> instead of something like IList<T>. I believe this will be (has been?) addressed in the next version of MVC due out with ASP.NET 4.0. For the time being, though, the MVC default model binder does not seem to understand how to work with EntitySet.

Please see this other answer I wrote on a related question some time ago, with details about how I am now dealing with (working around) this situation in certain simple cases. In doing this, I've had to stop worrying so much about how "ideal" this solution is from a DDD/OOP perspective, as a trade-off to simply getting MVC and LTS to play together nicely with minimal effort.

Note, I will admit that James S's solution is probably more "pure," but in order to do this in "one shot" as you ask, you'll need to either wait for ASP.NET 4.0, or use a workaround similar to what you'll find in my other post.

Good luck!

Funka