Wrox Programmer Forums

Need to download code?

View our list of code downloads.

Go Back   Wrox Programmer Forums > ASP.NET and ASP > ASP.NET 4 > BOOK: Professional ASP.NET MVC 2
Password Reminder
Register
Register | FAQ | Members List | Calendar | Search | Today's Posts | Mark Forums Read
BOOK: Professional ASP.NET MVC 2
This is the forum to discuss the Wrox book Professional ASP.NET MVC 2 by Jon Galloway, Scott Hanselman, Phil Haack, Scott Guthrie, Rob Conery; ISBN: Professional ASP.NET MVC 2
Welcome to the p2p.wrox.com Forums.

You are currently viewing the BOOK: Professional ASP.NET MVC 2 section of the Wrox Programmer to Programmer discussions. This is a community of tens of thousands of software programmers and website developers including Wrox book authors and readers. As a guest, you can read any forum posting. By joining today you can post your own programming questions, respond to other developers’ questions, and eliminate the ads that are displayed to guests. Registration is fast, simple and absolutely free .
DRM-free e-books 300x50
 
 
Thread Tools Search this Thread Display Modes
  #1 (permalink)  
Old October 13th, 2010, 03:12 PM
Registered User
 
Join Date: Oct 2010
Location: Zürich
Posts: 8
Thanks: 0
Thanked 1 Time in 1 Post
Default TryUpdateModel no longer saving edits

When I edit, say, the address of a dinner, and then post this back to my Edit() method, I can verify that the new address is in the formValues object, and when TryUpdateModel(dinner) is called it returns true, but the address is never changed.

If I do this manually, i.e.,
Code:
dinner.Address = formValues["Dinner.Address"];
then this works.

This is the same for all of the input fields / dinner-object properties.

Any suggestions as to why TryUpdateModel can't seem to match the properties with the posted form values?

(This was working earlier in my project.)

Last edited by ajameson; October 13th, 2010 at 03:22 PM..
  #2 (permalink)  
Old October 13th, 2010, 04:08 PM
Friend of Wrox
Points: 539, Level: 8
Points: 539, Level: 8 Points: 539, Level: 8 Points: 539, Level: 8
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Oct 2010
Location: Seattle
Posts: 106
Thanks: 1
Thanked 17 Times in 17 Posts
Default

In the controller, I had to use the overrided TryUpdateModel.

Code:
[HttpPost]
public ActionResult Edit(int id, FormCollection formValues)
{

Dinner dinner = dinnerRepository.GetDinner(id);
if (TryUpdateModel(dinner, "Dinner"))
{
......
}
  #3 (permalink)  
Old October 15th, 2010, 01:24 PM
Registered User
 
Join Date: Oct 2010
Location: Zürich
Posts: 8
Thanks: 0
Thanked 1 Time in 1 Post
Default

Once again, flyinhawaiian, I'm indebted.

Any idea why this is needed? Isn't it supposed to be intelligent enough to determine by itself which object it should reflect upon?
  #4 (permalink)  
Old October 15th, 2010, 02:50 PM
Friend of Wrox
Points: 539, Level: 8
Points: 539, Level: 8 Points: 539, Level: 8 Points: 539, Level: 8
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Oct 2010
Location: Seattle
Posts: 106
Thanks: 1
Thanked 17 Times in 17 Posts
Default

This is a side effect of a workaround that was done in the view. One of the fixes for page 89 suggested changing m.Title to m.Dinner.Title in the view. In this case we're reaching "deeper" through the Dinner to get to the title. Apparently The TryUpdateModel automatically knows about Dinner but not the properties inside the viewModel's Dinner object.

I actually solved this one differently. The book mentions making a custom shaped viewModel (but for some reason shows no example). I suspect that the book editors cut that part out. So, I changed my view model (customized it) to expose Title such that m.Title works the the view AND the TryUpdateModel didn't have to have to be modified.

So the alternative is to leave things as they were:
1. The view uses m.Title
2. TryUpdateModel doesnt have the "Dinner" prefix parameter

Then customize the shape of the ViewModel DinnerFormViewModel.cs
Code:
public string Title
{
    get	{ return Dinner.Title; }
    set { Dinner.Title = value; }
}

// etc...  but don't expose DinnerID.
by doing this, you abstract away Dinner. So the view has no idea a Dinner exists (could make it private), it just knows about the properties exposed via the ViewModel. It is therefore possible to hide certain properties of Dinner or even combine data from different models. Think "separation of concearns". In the event you want to use a different model in the future, you can change the ViewModel w/o messing with the view... maybe something like this
Code:
public string Title
{
    get { return MyThirdPartyDinner.Title; }    // Real implementation hidden from view.
    set { MyThirdPartyDinner.Title = value; }
}
// etc...
Note although making Dinner private works, later on when you implement Map.ascx, there are more adjustments to be made. This is because the book continues to use the Dinner object for the view (even though a ViewModel is available). So to keep it simple (KISS) I left my ViewModel's Dinner object public just to finish off the book.

Last edited by flyinhawaiian; October 15th, 2010 at 03:11 PM..
  #5 (permalink)  
Old December 30th, 2010, 01:29 AM
Registered User
 
Join Date: Dec 2010
Posts: 3
Thanks: 0
Thanked 0 Times in 0 Posts
Question still not saving edits now that DinnerFormViewModel implemented

I followed advice to create a shaped viewmodel per this thread -
I also validated the create & edit elements of dinnerscontroller as well as correcting the inheritance in edit.aspx and create.aspx - code as follows below. What am I missing?

Thanks for all the help in the forums!

DinnerFormViewModel.cs
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace NerdDinner.Models
{
    public class DinnerFormViewModel
    {
        private static string[] _countries = new[] {
            "USA",
            "Afghanistan",
            "Akrotiri",
            "Albania",
            "Zimbabee"
        };

        // Properties
        public Dinner Dinner { get; set; }

        public SelectList Countries { get; set; }

        public string Title
        {
            get { return Dinner.Title; }
            set { Dinner.Title = value; }
        }

        public DateTime EventDate
        {
            get { return Dinner.EventDate; }
            set { Dinner.EventDate = value; }
        }

        public string Description
        {
            get { return Dinner.Description; }
            set { Dinner.Description = value; }
        }

        public string Address
        {
            get { return Dinner.Address; }
            set { Dinner.Address = value; }
        }

        public string ContactPhone
        {
            get { return Dinner.ContactPhone; }
            set { Dinner.ContactPhone = value; }
        }

        public Double Latitude
        {
            get { return Dinner.Latitude; }
            set { Dinner.Latitude = value; }
        }

        public Double Longitude
        {
            get { return Dinner.Longitude; }
            set { Dinner.Longitude = value; }
        }

        // Constructor
        public DinnerFormViewModel(Dinner dinner)
        {
            Dinner = dinner;

            Countries = new SelectList(_countries, dinner.Country);
        }

    }
}
DinnersController.cs
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using NerdDinner.Models;

namespace NerdDinner.Controllers
{
    public class DinnersController : Controller
    {
        DinnerRepository dinnerRepository = new DinnerRepository();

        //
        // GET: /Dinners/

        public ActionResult Index()
        {
            var dinners = dinnerRepository.FindUpcomingDinners().ToList();

            return View(dinners);
        }

        //
        // GET: /Dinners/Details/2

        public ActionResult Details(int id)
        {
            Dinner dinner = dinnerRepository.GetDinner(id);
            if (dinner == null)
                return View("NotFound");
            else
                return View(dinner);
        }

        //
        // GET: /Dinners/Edit/2

        public ActionResult Edit(int id)
        {
            Dinner dinner = dinnerRepository.GetDinner(id);

            return View(new DinnerFormViewModel(dinner));
        }

        //
        // POST: /Dinners/Edit/2

        [HttpPost]
        public ActionResult Edit(int id, FormCollection formValues)
        {
            string[] allowedProperties = new[] { "Title", "Description", "ContactPhone", "Address", "Country", "EventDate", "Latitude", "Longitude" };
            Dinner dinner = dinnerRepository.GetDinner(id);

            if (TryUpdateModel(dinner, "Dinner"))
            {
                dinnerRepository.Save();
                return RedirectToAction("Details", new { id = dinner.DinnerID });
            }

            return View(new DinnerFormViewModel(dinner));
        }

        //
        // GET: /Dinners/Create

        public ActionResult Create()
        {
            Dinner dinner = new Dinner()
            {
                EventDate = DateTime.Now.AddDays(7)
            };
            return View(new DinnerFormViewModel(dinner));
        }

        //
        // POST: /Dinners/Create - [Bind(Include="Title,Address")]

        [HttpPost]
        public ActionResult Create(Dinner dinner)
        {
            if (ModelState.IsValid)
            {
                dinner.HostedBy = "SomeUser";

                dinnerRepository.Add(dinner);
                dinnerRepository.Save();

                return RedirectToAction("Details", new { id = dinner.DinnerID });
            }

            return View(new DinnerFormViewModel(dinner));
        }

        //
        // HTTP GET: /Dinners/Delete/1

        public ActionResult Delete(int id)
        {
            Dinner dinner = dinnerRepository.GetDinner(id);

            if (dinner == null)
                return View("NotFound");
            else
                return View(dinner);
        }

        //
        // HTTP POST: /Dinners/Delete/1

        [HttpPost]
        public ActionResult Delete(int id, string confirmButton)
        {
            Dinner dinner = dinnerRepository.GetDinner(id);

            if (dinner == null)
                return View("NotFound");

            dinnerRepository.Delete(dinner);
            dinnerRepository.Save();

            return View("Deleted");
        }
    }
}
Create.aspx
Code:
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<NerdDinner.Models.DinnerFormViewModel>" %>

<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
	Host a Dinner
</asp:Content>

<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Host a Dinner</h2>

    <% Html.BeginForm(); %>
        <%: Html.ValidationSummary("Please correct the errors and try again.") %>

        <fieldset>
            <p>
                <%: Html.LabelFor(m => m.Title) %>:<br />
                <%: Html.TextBoxFor(m => m.Title) %>
                <%: Html.ValidationMessageFor(m => m.Title, "*") %>
            </p>
            <p>
                <%: Html.LabelFor(m => m.EventDate) %>:<br />
                <%: Html.TextBoxFor(m => m.EventDate) %>
                <%: Html.ValidationMessageFor(m => m.EventDate, "*") %>
            </p>
            <p>
                <%: Html.LabelFor(m => m.Description) %>:<br />
                <%: Html.TextAreaFor(m => m.Description) %>
                <%: Html.ValidationMessageFor(m => m.Description, "*") %>
            </p>
            <p>
                <%: Html.LabelFor(m => m.Address) %>:<br />
                <%: Html.TextBoxFor(m => m.Address) %>
                <%: Html.ValidationMessageFor(m => m.Address, "*") %>
            </p>
            <p>
                <%: Html.LabelFor(m => m.Countries) %>:<br />
                <%: Html.DropDownListFor(m => m.Countries, Model.Countries) %>
                <%: Html.ValidationMessageFor(m => m.Countries, "*") %>
            </p>
            <p>
                <%: Html.LabelFor(m => m.ContactPhone) %>:<br />
                <%: Html.TextBoxFor(m => m.ContactPhone) %>
                <%: Html.ValidationMessageFor(m => m.ContactPhone, "*") %>
            </p>
            <p>
                <%: Html.LabelFor(m => m.Latitude) %>:<br />
                <%: Html.TextBoxFor(m => m.Latitude)%>
                <%: Html.ValidationMessageFor(m => m.Latitude, "*")%>
            </p>
            <p>
                <%: Html.LabelFor(m => m.Longitude)%>:<br />
                <%: Html.TextBoxFor(m => m.Longitude)%>
                <%: Html.ValidationMessageFor(m => m.Longitude, "*")%>
            </p>
            <p>
                <input type="submit" value="Save" />
            </p>
        </fieldset>
    <% Html.EndForm(); %>

    <div>
        <%: Html.ActionLink("Back to List", "Index") %>
    </div>

</asp:Content>
Edit.aspx
Code:
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<NerdDinner.Models.DinnerFormViewModel>" %>

<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
	Edit: <%: Model.Title %>
</asp:Content>

<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Edit Dinner</h2>

    <% Html.BeginForm(); %>
        <%: Html.ValidationSummary("Please correct the errors and try again.") %>
        
        <fieldset>
            <legend>Fields</legend>
            
            <div class="editor-label">
                <%: Html.LabelFor(m => m.Title) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(m => m.Title) %>
                <%: Html.ValidationMessageFor(m => m.Title, "*") %>
            </div>
            
            <div class="editor-label">
                <%: Html.LabelFor(m => m.EventDate) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(m => m.EventDate) %>
                <%: Html.ValidationMessageFor(m => m.EventDate, "*") %>
            </div>
            
            <div class="editor-label">
                <%: Html.LabelFor(m => m.Description) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(m => m.Description) %>
                <%: Html.ValidationMessageFor(m => m.Description, "*") %>
            </div>
            
            <div class="editor-label">
                <%: Html.LabelFor(m => m.Address) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(m => m.Address) %>
                <%: Html.ValidationMessageFor(m => m.Address, "*") %>
            </div>
            
            <div class="editor-label">
                <%: Html.LabelFor(m => m.Countries) %>
            </div>
            <div class="editor-field">
                <%: Html.DropDownListFor(m => m.Countries, Model.Countries) %>
                <%: Html.ValidationMessageFor(m => m.Countries, "*") %>
            </div>
            
            <div class="editor-label">
                <%: Html.LabelFor(m => m.ContactPhone) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(m => m.ContactPhone) %>
                <%: Html.ValidationMessageFor(m => m.ContactPhone, "*") %>
            </div>
            
            <div class="editor-label">
                <%: Html.LabelFor(m => m.Latitude) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(m => m.Latitude) %>
                <%: Html.ValidationMessageFor(m => m.Latitude, "*") %>
            </div>
            
            <div class="editor-label">
                <%: Html.LabelFor(m => m.Longitude) %>
            </div>
            <div class="editor-field">
                <%: Html.TextBoxFor(m => m.Longitude) %>
                <%: Html.ValidationMessageFor(m => m.Longitude, "*") %>
            </div>
            
            <p>
                <input type="submit" value="Save" />
            </p>
        </fieldset>

    <% Html.EndForm(); %>

</asp:Content>
  #6 (permalink)  
Old December 30th, 2010, 02:56 AM
Registered User
 
Join Date: Dec 2010
Posts: 3
Thanks: 0
Thanked 0 Times in 0 Posts
Default Correction

After twisting a couple things around so that I had country straightened out, everything works. Thanks anyway.
  #7 (permalink)  
Old December 30th, 2010, 02:57 AM
Friend of Wrox
Points: 539, Level: 8
Points: 539, Level: 8 Points: 539, Level: 8 Points: 539, Level: 8
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Oct 2010
Location: Seattle
Posts: 106
Thanks: 1
Thanked 17 Times in 17 Posts
Default

Can you clarify your workflow... did you create a dinner and then try to edit and save? Or are you having problems creating a dinner?

If you put a breakpoint on the TryUpdate and run in debug, do you hit the breakpoint? If you do, does the dinner object have updated values? If so, start digging into the repository.

I can compare with my code, except it's many many miles away. Won't be able to get to it until Monday morning.
  #8 (permalink)  
Old December 30th, 2010, 02:58 AM
Friend of Wrox
Points: 539, Level: 8
Points: 539, Level: 8 Points: 539, Level: 8 Points: 539, Level: 8
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Oct 2010
Location: Seattle
Posts: 106
Thanks: 1
Thanked 17 Times in 17 Posts
Default

I see you figured it out already. Good for you.
  #9 (permalink)  
Old January 22nd, 2011, 03:35 PM
Registered User
 
Join Date: Jan 2011
Posts: 13
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Quote:
Originally Posted by JMStheReal View Post
After twisting a couple things around so that I had country straightened out, everything works. Thanks anyway.
Hi JMS: Thanks for posting your solution...I tried modifying my pages as per your code, but my update is still failing. What did you do finally to get it to work?

Or can anyone help me figure out how to debug what's happening in the TryUpdateModel?

EDIT: I reverted the code back to "UpdateModel(dinner)" from "UpdateModel(dinner, "Dinner")" and now it works (i.e. the form saves properly). I'm really newbie at this, so if anyone can explain to me why it works one way but not the other, I would appreciate it!

Last edited by ebmudder; January 22nd, 2011 at 04:03 PM..
  #10 (permalink)  
Old January 22nd, 2011, 08:39 PM
Registered User
 
Join Date: Jan 2011
Posts: 13
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Quote:
Originally Posted by ebmudder View Post
Hi JMS: Thanks for posting your solution...I tried modifying my pages as per your code, but my update is still failing. What did you do finally to get it to work?

Or can anyone help me figure out how to debug what's happening in the TryUpdateModel?

EDIT: I reverted the code back to "UpdateModel(dinner)" from "UpdateModel(dinner, "Dinner")" and now it works (i.e. the form saves properly). I'm really newbie at this, so if anyone can explain to me why it works one way but not the other, I would appreciate it!
Well, it _almost_ works
The only form value that isn't being picked up is the "Countries" select list. I'm struggling with trying to figure out how to get the "Countries" selectlist value be mapped properly to the dB field "Country"?
 


Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off
Trackbacks are Off
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
WPF infragiscs date picker no edits no nulls Burledivya WinForms/Console Application Design 0 February 2nd, 2010 10:20 AM
Allow Edits In SubForm tready Access VBA 1 December 19th, 2005 08:41 AM
JavaScript and Date Edits srotondo Javascript How-To 3 February 2nd, 2005 01:32 AM
Form Data Edits bph Access VBA 6 January 26th, 2004 11:12 AM
Access edits kev_79 Access ASP 2 September 23rd, 2003 11:50 PM



All times are GMT -4. The time now is 09:21 PM.


Powered by vBulletin®
Copyright ©2000 - 2019, Jelsoft Enterprises Ltd.
© 2013 John Wiley & Sons, Inc.