I found little bug feature in MVC .
So, if we have in route defined parameter with same name as property at model and then we call it from strongly typed HTML helper (for example Html.TexBoxFor(x => x.PropertyName)), we get value from route parameter instead of property of model.
For example.
We have defined follow route:
routes.MapRoute(
null, // Route name
"{controller}/users/{username}", // URL with parameters
new { controller = "Home", action = "UserDetail" } // Parameter defaults
);
Very simple model:
namespace MvcApplication.Model
{
public class UserViewModel
{
public string Username { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
public string Email { get; set; }
}
}
Next action in controller:
public ActionResult UserDetails(string username)
{
var user = new UserViewModel
{
Username = "Haacked",
Email = "blabla@mail.com",
Firstname = "Phil",
Lastname = "Haack"
};
return View(user);
}
This is actually the correct behavior. MVC has a concept called ModelState (accessible via the Controller.ModelState property) which contains information about the model the MVC request is currently working with. The ModelState collection stores raw values
submitted by the user and a list of validation errors for each of those values.
When displaying a form, all HTML helpers look in ModelState first to get the value that should be displayed to the user, then they look at the actual model itself if ModelState doesn't contain anything useful. The reason for this is so that user input isn't
blasted away in the event of a conversion error. For example, say that the user types "Decembr 21, 1998" (note the misspelling) for a textbox that corresponds to a DateTime property on your model. When the form is redisplayed to the user in error, the textbox
will contain the original text "Decembr 21, 1998" (which can't be represented by a DateTime) rather than the model property's actual value of default(DateTime), which is "January 1, 0001".
Now, how is this relevant to your case? Your action method takes a parameter called
username. The existence of this parameter signals to MVC that username
is user input that should be added to ModelState, thus the helpers treat username
parameter with a higher precedence than the actual model itself.
If you did not intend this, remove the username parameter from your action method. This will prevent the MVC pipeline from treating
username as user input and thus prevent the parameter from propagating to the form. If for some reason you cannot remove the parameter, call ModelState.Clear() as the first line in your action method. This tells the MVC pipeline that there are no user
input values for the current request. Or change the route definition such that it doesn't contain a
{username} path segment, as the username appears to be unused by the current request.
banguit
Member
10 Points
1 Post
Model Binding Precedence
May 18, 2010 05:44 PM|banguit|LINK
Hi guys!
I found little bug feature in MVC .
So, if we have in route defined parameter with same name as property at model and then we call it from strongly typed HTML helper (for example Html.TexBoxFor(x => x.PropertyName)), we get value from route parameter instead of property of model.
For example.
We have defined follow route:
routes.MapRoute( null, // Route name "{controller}/users/{username}", // URL with parameters new { controller = "Home", action = "UserDetail" } // Parameter defaults );Very simple model:
namespace MvcApplication.Model { public class UserViewModel { public string Username { get; set; } public string Firstname { get; set; } public string Lastname { get; set; } public string Email { get; set; } } }Next action in controller:
public ActionResult UserDetails(string username) { var user = new UserViewModel { Username = "Haacked", Email = "blabla@mail.com", Firstname = "Phil", Lastname = "Haack" }; return View(user); }And very simple view:
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<MvcApplication.Models.UserViewModel>" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>User Details</title> </head> <body> <div class="display-label">Username</div> <div class="display-field"><%= Html.TextBoxFor(x => x.Username) %></div> <div class="display-label">Firstname</div> <div class="display-field"><%= Html.TextBoxFor(x => x.Firstname)%></div> <div class="display-label">Lastname</div> <div class="display-field"><%= Html.TextBoxFor(x => x.Lastname)%></div> <div class="display-label">Email</div> <div class="display-field"><%= Html.TextBoxFor(x => x.Email) %></div> </body> </html>Result you can see at follow picture:

I think what this dirty example described the essence of the problem. Let me know if I'm wrong.
Thanks,
Dima
levib
Contributor
5615 Points
1111 Posts
Re: Model Binding Precedence
May 18, 2010 07:35 PM|levib|LINK
Thanks for the report.
This is actually the correct behavior. MVC has a concept called ModelState (accessible via the Controller.ModelState property) which contains information about the model the MVC request is currently working with. The ModelState collection stores raw values submitted by the user and a list of validation errors for each of those values.
When displaying a form, all HTML helpers look in ModelState first to get the value that should be displayed to the user, then they look at the actual model itself if ModelState doesn't contain anything useful. The reason for this is so that user input isn't blasted away in the event of a conversion error. For example, say that the user types "Decembr 21, 1998" (note the misspelling) for a textbox that corresponds to a DateTime property on your model. When the form is redisplayed to the user in error, the textbox will contain the original text "Decembr 21, 1998" (which can't be represented by a DateTime) rather than the model property's actual value of default(DateTime), which is "January 1, 0001".
Now, how is this relevant to your case? Your action method takes a parameter called username. The existence of this parameter signals to MVC that username is user input that should be added to ModelState, thus the helpers treat username parameter with a higher precedence than the actual model itself.
If you did not intend this, remove the username parameter from your action method. This will prevent the MVC pipeline from treating username as user input and thus prevent the parameter from propagating to the form. If for some reason you cannot remove the parameter, call ModelState.Clear() as the first line in your action method. This tells the MVC pipeline that there are no user input values for the current request. Or change the route definition such that it doesn't contain a {username} path segment, as the username appears to be unused by the current request.
Hope this helps!
ricka6
Star
12512 Points
2217 Posts
Microsoft
Moderator
Re: Model Binding Precedence
Jul 25, 2011 04:03 PM|ricka6|LINK
See also Html.Hidden ignores given value