views:

58

answers:

1

Googling generally and on SO hasn't helped me yet, so:

I am building my first MVC application from scratch, going by the MVC Music Store example but instead building a little application where arena Fighters can be created and made to Fight each other. (Fighters and Fight have been made linked to underlying tables through EF).

I have controllers for both the Fighters and the Fights. The Edit Actionresult for Fights is working, but for Fighters it is not. When I hit the button to save my alterations I return to the associated Index page, but no changes have been committed. This is my question: why is this failing?

From BarracksController, with the faulty non-updating HttpPost Edit (should have been named FighterController, but neverthemind):

        //
        // GET: /Barracks/Edit
        public ActionResult Edit(int id)
        {
            ViewData.Model = _FightDb.Fighters.Single(f => f.Id == id);
            return View();
        }


        //
        // POST: /Barracks/Edit
        [HttpPost]
        public ActionResult Edit(int id, FormCollection collection)
        {
            var fighter = _FightDb.Fighters.Single(f => f.Id == id);

            try
            {
                UpdateModel(fighter, "Fighter");
                var x = ViewData.GetModelStateErrors();
                _FightDb.SaveChanges();

                return RedirectToAction("Index");
            }
            catch
            {
                var viewModel = fighter;

                return View(viewModel);
            }

        }

(As you can see, I've included the GetModelStateErrors trick from this SO question, but the result for x is null)

This is the controller that does work, FightController:

   //
        // GET: /Fights/Edit
        public ActionResult Edit(int id)
        {
            var viewModel = new FightDetailsViewModel
            {
                Fight = _FightDb.Fights.Single(f => f.ID == id),
                Fighters = _FightDb.Fighters.ToList()
            };

            return View(viewModel);
        }

        //
        // POST: /Fights/Edit
        [HttpPost]
        public ActionResult Edit(int id, FormCollection collection)
        {
            var fight = _FightDb.Fights.Single(f => f.ID == id);

            try
            {
                UpdateModel(fight, "Fight");
                _FightDb.SaveChanges();

                return RedirectToAction("Index");
            }
            catch
            {
                var viewModel = new FightDetailsViewModel
                {
                    Fight = _FightDb.Fights.Single(f => f.ID == id),
                    Fighters = _FightDb.Fighters.ToList()
                };

                return View(viewModel);
            }
        }

This is edit.aspx for the Fighters: (Edited after comment)

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Mvc3_EF_BW_Fight.Models.Fighter>" %>


<asp:Content ID="Content1" ContentPlaceHolderID="cphMain" runat="server">
    <h2>Edit</h2>

        <%: Html.EditorForModel()  %>

</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="head" runat="server">
</asp:Content>

Which uses the following Fighter.ascx in Shared:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Mvc3_EF_BW_Fight.Models.Fighter>" %>
<% using (Html.BeginForm())
   {%>
<%: Html.ValidationSummary(true) %>
<fieldset>
    <legend>Fighter</legend>
    <div class="editor-label">
        <%: Html.LabelFor(model => model.Id) %>
    </div>
    <div class="editor-field">
        <%: Html.TextBoxFor(model => model.Id) %>
        <%: Html.ValidationMessageFor(model => model.Id) %>
    </div>
    <div class="editor-label">
        <%: Html.LabelFor(model => model.FighterName) %>
    </div>
    <div class="editor-field">
        <%: Html.TextBoxFor(model => model.FighterName) %>
        <%: Html.ValidationMessageFor(model => model.FighterName) %>
    </div>
    <div class="editor-label">
        <%: Html.LabelFor(model => model.FighterStyleDescription) %>
    </div>
    <div class="editor-field">
        <%: Html.TextBoxFor(model => model.FighterStyleDescription) %>
        <%: Html.ValidationMessageFor(model => model.FighterStyleDescription) %>
    </div>
    <div class="editor-label">
        <%: Html.LabelFor(model => model.FighterLongDescription) %>
    </div>
    <div class="editor-field">
        <%: Html.TextAreaFor(model => model.FighterLongDescription) %>
        <%: Html.ValidationMessageFor(model => model.FighterLongDescription) %>
    </div>
    <p>
        <input type="submit" value="Save" />
    </p>
</fieldset>
<% } %>

This is the edit.aspx for Fights

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Mvc3_EF_BW_Fight.ViewModels.FightDetailsViewModel>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="cphMain" runat="server">

    <h2>Edit</h2>
    <%: Html.EditorFor(model => model.Fight, new { Fighters = Model.Fighters })%>

</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="head" runat="server">
</asp:Content>

And this is the Fight.ascx:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Mvc3_EF_BW_Fight.Models.Fight>" %>
<% using (Html.BeginForm())
   {%>
<%: Html.ValidationSummary(true) %>
<fieldset>
    <legend>Fields</legend>
    <div class="editor-label">
        <%: Html.LabelFor(model => model.ID) %>
    </div>
    <div class="editor-field">
        <%: Html.TextBoxFor(model => model.ID) %>
        <%: Html.ValidationMessageFor(model => model.ID) %>
    </div>


    <div class="editor-label">
        <%: Html.LabelFor(model => model.FightName) %>
    </div>
    <div class="editor-field">
        <%: Html.TextBoxFor(model => model.FightName) %>
        <%: Html.ValidationMessageFor(model => model.FightName) %>
    </div>
    <div class="editor-label">
        <%: Html.LabelFor(model => model.Fighter1ID) %><br />
        <%: Html.LabelFor(model => model.Fighter1Reference.Value.FighterName)%>
    </div>
    <div class="editor-field">
        <%: Html.DropDownList("Fighter1ID", new SelectList(ViewData["Fighters"] as IEnumerable, "ID", "FighterName", Model.Fighter1ID))%>
        <%: Html.ValidationMessageFor(model => model.Fighter1ID) %>
    </div>
    <div class="editor-label">
        <%: Html.LabelFor(model => model.Fighter2ID) %>
    </div>
    <div class="editor-field">
        <%: Html.DropDownList("Fighter1ID", new SelectList(ViewData["Fighters"] as IEnumerable, "ID", "FighterName", Model.Fighter1ID))%>
        <%: Html.ValidationMessageFor(model => model.Fighter2ID) %>
    </div>
    <div class="editor-label">
        <%: Html.LabelFor(model => model.Fighter1Login) %>
    </div>
    <div class="editor-field">
        <%: Html.TextBoxFor(model => model.Fighter1Login) %>
        <%: Html.ValidationMessageFor(model => model.Fighter1Login) %>
    </div>
    <div class="editor-label">
        <%: Html.LabelFor(model => model.Fighter2Login) %>
    </div>
    <div class="editor-field">
        <%: Html.TextBoxFor(model => model.Fighter2Login) %>
        <%: Html.ValidationMessageFor(model => model.Fighter2Login) %>
    </div>
    <div class="editor-label">
        <%: Html.LabelFor(model => model.FightStatusID) %>
    </div>
    <div class="editor-field">
        <%: Html.TextBoxFor(model => model.FightStatusID) %>
        <%: Html.ValidationMessageFor(model => model.FightStatusID) %>
    </div>
    <p>
        <input type="submit" value="Save" />
    </p>
</fieldset>
<% } %>

And this is my viewmodel for Fights:

public class FightDetailsViewModel
{
    public Fight Fight { get; set; }
    public List<Fighter> Fighters { get; set; }
}

There is no ViewModel for Fighters (none that is involved in this scenario, anyway).

I can post any code you may wish to see.

Edit: I've looked at http://stackoverflow.com/questions/2160171/using-viewmodel-pattern-with-mvc-2-strongly-typed-html-helpers and http://stackoverflow.com/questions/2936927/asp-net-mvc-2-updatemodel-is-not-updating-values-in-memory-or-database , but i haven't seen a solution there yet.

+2  A: 

Instead of this UpdateModel(fighter, "Fighter"); try calling the updte model just like this UpdateModel(fighter);. The difference is between the two edits that in case of Fighter your model is directly the Fighter so you do not need the name, while in case of the Fight you call the Editor for model.Fight so you need the name. See this question as well: http://stackoverflow.com/questions/3147739/asp-net-mvc2-how-to-get-model-and-model-something-in-the-same-way-in-controller/3148156#3148156

apolka
I believe I've tried that permutation as well, but I'll make sure later when I'm behind the appropriate PC. Thanks.A bit odd though, that the more declarative syntax shouldn't work where the simpeler syntax does (if your answer turns out to be correct)?
Tobiasopdenbrouw
It is not about trying permutations randomly. In one case it will work only if you define the prefix in the other it will work only if you do not. It depends upon if you want to bind an object (no prefix needed) or object.something (prefix needed). So let me know if you could fix the issue...
apolka
I'm not trying permutations randomly (I may be new, but not THAT new). It's just that the difference in UpdateModel calls (1 param or 2) is (was), in my mind, 0. It's like the second parameter is a specification that is required only if you're not using the default, but specifying the default (`"Fighter"`, in my case) should be the same as not specifying it. But from reading you, what I'm actually doing is looking for `Fighter.Fighter` the way I'm doing it right now, which would be wrong. I'll get back to you ASAP when I'm behind the right machine (which should be in a few hours).
Tobiasopdenbrouw
And that worked. Thanks!
Tobiasopdenbrouw