views:

335

answers:

1

Hello gurus,

I have a view to create a user as follows:

<% using (Html.BeginForm("SaveUser", "Security")) {%>
            <p>
            <label for="UserName">UserName:</label>
            <%= Html.TextBox("UserName") %>
            <%= Html.ValidationMessage("UserName", "*") %>
        </p>
        <p>
            <label for="Password">Password:</label>
            <%= Html.TextBox("Password") %>
            <%= Html.ValidationMessage("Password", "*") %>
        </p>
        <p>
            <input type="submit" value="Create" />
        </p>
<}%>

When the "Create" button is clicked, the HTML form is posted to an action called "SaveUser" that accepts "POST" verb only as follows:

[AcceptVerbs(HttpVerbs.Post)]
  public ActionResult SaveUser(UserViewModel user)
  {
   //user.Id is zero before save
   //Save the user.  Code omitted...
   //user.Id is now greater than zero
   //redirect to edit user view
   return View("EditUser", user );
  }

After the user is saved, the page is redirected to the "EditUser" view with

            <p>
            <label for="Id">Id:</label>
            <%= Html.Hidden("Id", Model.Id)%>
        </p>

Here is the problem: the value for the hidden field kept showing up as zero though Model.Id is greater than zero. It seemed that something else is overriding the model view value. ViewDataDictonary was a suspect. So a line is added before returning the view in the action as follows:

[AcceptVerbs(HttpVerbs.Post)]
  public ActionResult SaveUser(UserViewModel user)
  {
   //user.Id is zero before save
   //Save the user.  Code omitted...
   //user.Id is now greater than zero

   //clear the view data
   ViewData = new ViewDataDictionary();
   //redirect to edit user view
   return View("EditUser", user );
  }

Sure enough, this worked. The hidden field now has a value of the correct user id.

We found a way to treat the sympotum but: Where is the source of the problem?

I don't like the idea of clearing view data dictionary every time before a returning another view.

If any guru could enlighten me, I'd be most grateful?

Thanks,

Cullen

+4  A: 

After successful operation You should use

return RedirectToAction("EditUser", new { id = user.Id });

or similar code. Current ModelState is used to generate view and model binder didn't bound Id.

[Bind(Exclude = "Id")] could also work, but redirection creates new page (not using current ModelState) and is better solution.

Edit:

If You don't want to bind whole object, You should use [Bind (Exclude)] or You should just define SaveUser as SaveUser(string userName, string password) and build UserViewModel object yourself. This will save You from errors generated by model binder and Model values, that You don't know where they come from:)

LukLed
++, this is the preferred way of handling this. It also solves the problem of the user refreshing the page and inadvertently re-posting the same form post. Depending on the system, that could be a bad thing if your action isn't idempotent.
Joel Martinez
@LukLed, @Joel: thanks guys for the helpful.My UserViewModel is more complex than I posted here so I've written custom model binder. A friend of mine found a way to use the default binder without the custom one so I'll work on switching to it.
Cullen Tsering
@LukLed: after using RedirectToAction, the problem still occurred and I had to use ViewData = new ViewDataDictionary(); In other words, using redirecting is not solving the problem. Is there something I'm missing?
Cullen Tsering
It is weird. Can You show us some code?
LukLed