In general, you should never be creating a ValueProviderResult; you should be getting it from the dictionary provided by
ModelBindingContext.ValueProvider. The reason that the culture is specified per-value isn't that textbox A has Culture A and textbox B has Culture B; it's that we need to know
where the value came from.
Consider the case of ValueProvider["someDate"].ConvertTo(typeof(DateTime)). If
someDate is specified in the URL (as it would be if it were a Routing parameter), this conversion should be done in a culture-invariant fashion. After all, the U in URL is for
uniform. If I were in the U.S. and IMed my friend in the U.K. a URL containing a date, it shouldn't produce a 404 for him just because he's coming from a different country. However, if
someDate were a form value (that is, we read it from Request.Form instead of Routing), you want to perform the conversion in a culture-aware fashion, because the user should be able to input form entries using his current locale's settings. In general,
values from Routing and the query string (which together make the URL) are InvariantCulture, while values from Form are CurrentCulture.
OK - so this explains the CultureInfo, but what about the RawValue property? This has to do with the way in which we fetch values from the underlying mechanism - Routing, QueryString, or Form. Routing uses a RouteValueDictionary, a dictionary in which
the entries are of type Object. QueryString and Form are internally NameValueCollections, and we call GetValues(), which returns a string array. This is what the RawValue returns - the result as given to us by the underlying mechanism, unmodified.
The RawValue and CultureInfo are both consumed by the ConvertTo() method, which can perform the conversion using the correct culture and give you the type you wanted. The AttemptedValue is only used by the validation framework; it's used for providing an
error message if a submitted value is incorrect.
Thanks for the information. To me it doesn't really make sense not to have the AddModelError and SetModelValue together in a method. (Like the binder as you suggested to hvossos in an earlier post). OK, it's good that you don't want to force where/how value-binding
and validation takes place. However, splitting up this into two different parts of your application, would be very confusing to developers. First you had to go look in the validation code, looking for an AddModelError() call and then search the value-binding
for a SetModelValue() with the same key. And, why would I be going into the value-binding method if my validation fails? It's kind of hard to bind to a DateTime when it's not possible to convert it to a DateTime :) Big thank you for explaining about the CultureInfo,
it didn't make any sense and when looking through the ASP.NET MVC code it was always invoked with null (so it would use the culture-invariant).
In my opinion, there shouldn't be any "Model Value", all the model value should explicitly come from the controller.
When I wrote <%=Html.TextBox("Name") %>, I mean find the "Name" property/field from ViewPage.Model (or ViewData dictionary), and retrieve the value, and use it as the value for html input tag. Why the hell should I call SetModelValue after I modify the model
object which will be passed to the View? Otherwise I have to keep track of every modification of the model object -- it's possible that the class which modifies the model object knows nothing about System.Web.Mvc.
levib
Star
7702 Points
1099 Posts
Microsoft
Re: Custom ModelBinder and Release Candidate
Feb 02, 2009 04:27 AM|LINK
In general, you should never be creating a ValueProviderResult; you should be getting it from the dictionary provided by ModelBindingContext.ValueProvider. The reason that the culture is specified per-value isn't that textbox A has Culture A and textbox B has Culture B; it's that we need to know where the value came from.
Consider the case of ValueProvider["someDate"].ConvertTo(typeof(DateTime)). If someDate is specified in the URL (as it would be if it were a Routing parameter), this conversion should be done in a culture-invariant fashion. After all, the U in URL is for uniform. If I were in the U.S. and IMed my friend in the U.K. a URL containing a date, it shouldn't produce a 404 for him just because he's coming from a different country. However, if someDate were a form value (that is, we read it from Request.Form instead of Routing), you want to perform the conversion in a culture-aware fashion, because the user should be able to input form entries using his current locale's settings. In general, values from Routing and the query string (which together make the URL) are InvariantCulture, while values from Form are CurrentCulture.
OK - so this explains the CultureInfo, but what about the RawValue property? This has to do with the way in which we fetch values from the underlying mechanism - Routing, QueryString, or Form. Routing uses a RouteValueDictionary, a dictionary in which the entries are of type Object. QueryString and Form are internally NameValueCollections, and we call GetValues(), which returns a string array. This is what the RawValue returns - the result as given to us by the underlying mechanism, unmodified.
The RawValue and CultureInfo are both consumed by the ConvertTo() method, which can perform the conversion using the correct culture and give you the type you wanted. The AttemptedValue is only used by the validation framework; it's used for providing an error message if a submitted value is incorrect.
bjn
Member
4 Points
2 Posts
Re: Custom ModelBinder and Release Candidate
Feb 02, 2009 06:04 AM|LINK
deerchao
Member
126 Points
39 Posts
Re: Custom ModelBinder and Release Candidate
Feb 02, 2009 07:20 AM|LINK
In my opinion, there shouldn't be any "Model Value", all the model value should explicitly come from the controller.
When I wrote <%=Html.TextBox("Name") %>, I mean find the "Name" property/field from ViewPage.Model (or ViewData dictionary), and retrieve the value, and use it as the value for html input tag. Why the hell should I call SetModelValue after I modify the model object which will be passed to the View? Otherwise I have to keep track of every modification of the model object -- it's possible that the class which modifies the model object knows nothing about System.Web.Mvc.
Be and awear of who you are.