asp.net MVC: TryUpdateModel doesn't bind DateTime properly - asp.net-mvc-4

I'm having issues with a date property not binding correctly using TryUpdateModel in MVC.
I am using a POCO class and a corresponding viewModel.
public class ContactModel
{
public int Id { get; set; }
[Display(Name = "First Name")]
[StringLength(50)]
[Required(ErrorMessage = "First name must be entered.")]
public string ContactGivenName { get; set; }
[Display(Name = "Last Name")]
[StringLength(50)]
[Required(ErrorMessage = "Last name must be entered.")]
public string ContactFamilyName { get; set; }
....
[Display(Name = "Date of Birth")]
public DateTime? DateOfBirth { get; set; }
}
the entity class:
public class Contact
{
[Key]
public int Id { get; set; }
[StringLength(50)]
[Column(TypeName = "varchar")]
public string ContactFamilyName { get; set; }
[StringLength(50)]
[Column(TypeName = "varchar")]
public string ContactGivenName { get; set; }
...
[Column(TypeName = "date")]
public DateTime? DateOfBirth { get; set; }
}
and in my controller :
[HttpPost]
[GridAction]
public virtual ActionResult UpdateContact(int id, FormCollection form)
{
//Find a customer whose CustomerID is equal to the id action parameter
var c = _contactService.Get(id);
if (c != null)
{
//Perform model binding (fill the customer properties and validate it).
if (TryUpdateModel(c, form))
{
The _contactService.Get returns the instance from EntityFramework.
The TryUpdateModel binds string properties like first and last name correctly, but despite a lot of tries, I can't get it to bind the date of birth.
I suspect it's a formatting issue of some kind but cannot find what it is.
When I debug, I can see in the FormCollection that the date is correctly there, with the value that was input in the view so the data is definitely passed on to the controller.
the value passed to the controller is in format :
yyyy-MM-dd
I tried modifying this on the fly with multiple format without success.
Any ideas what I'm doing wrong here ?
Thanks a lot

Well, first, I have no idea why you're using TryUpdateModel.. That's pretty archaic.
Second, it sounds to me like you have not set your culture to accept the format you expect. Add the following to your web.config if your browser is defaults already set to the correct culture:
<system.web>
<globalization culture="auto" uiculture="auto" enableclientbasedculture="true">
</globalization>
</system.web>
If you want to force a culture, then set the culture you wish in this setting.

the globalization didn't help and is actually already set, however I realized there's a custom binder for Datetime and that is probably where my problem is.
thanks

Related

Why Required attribute in model validation works for some but not for some other?

I have following Controller Post method:
[Route("")]
[ResponseType(typeof(CardPost))]
[HttpPost]
public IHttpActionResult PostCard([FromBody] CardPost CardMetaData)
{
if (!ModelState.IsValid)
{
BadRequest(ModelState);
}//Property is not caught by ModelState
if (CardMetaData.Property == 0)
{
return BadRequest();
}
//Else insert to DBContext
}
I'm trying to bind and validate data using following Model class:
class CardPost
{
[Required(ErrorMessage = "property is required")]
[JsonProperty("property")]
public int Property { get; set; }
[Required(ErrorMessage = "Card Nr. is required")]
[StringLength(6,ErrorMessage ="Card Nr. is 6 characters")]
[JsonProperty("number")]
public string Number{ get; set; }
[Required(ErrorMessage ="Card Code is required")]
[JsonProperty("code")]
public string Code{ get; set; }
[Required(ErrorMessage = "blocked is required")]
[JsonProperty("blocked")]
public bool Blocked { get; set; }
}
Required attribute works fine for Number and Code but Property and Blocked nevers throw Exception even if not included in POST request.
A workaround is to do manuel validation as above but I wonder what is behind this?
The problem is showing only in one of my Controllers.
Properties are evaluated after setting the default value, so an int with a [Required] decoration does nothing because the default value of int is 0. So even if the property does not exist on the payload, no validation will be triggered. If the property is type of string or int?, then passing null or not including the property on the payload will trigger a validation error.
Option 1 : declare int as nullable type- int? Property
Option 2 : use [BindRequired] annotation

check empty or invalid properties

I have a model with validation properties. On a post back to server I would like to check if model values are empty or for example dropdownlist should not be have 0 or something!
Depending on how you have created your models you could do the validation on he model properties.
Example:
public class Foo
{
public long Id { get; set; }
[Required(ErrorMessage = "Please enter name")]
public string Name { get; set; }
[Required(ErrorMessage = "Please select a gender")]
public string SelectedGenderId{ get; set; }
}
Then in your view under your dropDownList:
#Html.ValidationMessageFor(m=> m.SelectedGenderId)
EDIT thanks to #danludwig:
Then in your controller you can validate the state of your model but doing this:
if (ModelState.IsValid)
{
// do your updates/saves
}
I hope that's is what you are after.

SelectList when using EditorFor

i have come across a strange behaviour when trying to get a SelectList to display correctly when using the Html.EditorForModel().
I have the following Model:
public class LoginModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
public string Gender { get; set; }
[UIHint("SelectList")]
public SelectList AvailibleGender { get; set; }
}
As you can see, I have a SelectList item that holds the values to get put into the Gender string. I have overriden the SelectList EditorTemplate, which for now has a simple script tag showing an alert box.
When I use EditorFor(m=>AvailableGender), my alert box fires, which means the editor correctly chose my SelectList template.
When I use EditorForModel(), my alert box does not fire.
If I change the model as follows:
[UIHint("SelectList")]
public string AvailibleGender { get; set; }
..then my SelectList template does get hit, and I see the alert message.
So it looks like there is something specifically blocking the SelectList.
In case it is something to do with specific custom types, I tried adding an object.cshtml file, but this doesn't get invoked either. so it seems that the SelectList type is getting lost somewhere.
Does anyone have any ideas on how to get the Select List working with EditorForModel()?

MVC4 Mailgun delivered webhook

I'm having an issue making use of the Mailgun delivered webhook, it can be found here: http://documentation.mailgun.net/user_manual.html#events-webhooks, look for "Delivered Event Webhook"
I am unable to reference Request.Params["Message-Id"] unless I modify the app's requestValidationMode to 2.0
I do get the potentially unsafe error when trying to reference this field without requestValidationMode = 2.0. The contents of the field are: <20130203200110.12345.12345#mydomain.mailgun.org>. I've also tried to declare a model to take advantage of auto model binding. My model looks like this:
public class MailgunDeliveredEvent
{
public string Id { get; set; }
public string Event { get; set; }
public string Recipient { get; set; }
public string Domain { get; set; }
[AllowHtml]
[JsonProperty(PropertyName="Message-Id")]
public object MessageId { get; set; }
public int Timestamp { get; set; }
public string Token { get; set; }
public string Signature { get; set; }
}
When I attempt to reference the MessageId field it returns null. I've tried to add
[Bind(Exclude="message-headers")]
As I'm not interested in that field.
In the Controller, I've set
[ValidateInput(false)]
I can't seem to get the Message-Id field back. Any help?
I seem to have got it working, in case anyone runs into the same issue...
I added a new model binder as referenced here:
Asp.Net MVC 2 - Bind a model's property to a different named value
I then changed my model like so:
[ModelBinder(typeof(DefaultModelBinderEx))]
public class MailgunDeliveredEvent
{
public string Id { get; set; }
public string Event { get; set; }
public string Recipient { get; set; }
public string Domain { get; set; }
[BindAlias("Message-Id")]
public string MessageId { get; set; }
public int Timestamp { get; set; }
public string Token { get; set; }
public string Signature { get; set; }
}
And all seems to work, I didn't need to call
[ValidateInput(false)]
on the controller either.
Hope that helps someone.

EditorTemplates > bool - bool.cshtml, DateTime? - DateTime?.cshtml (fail)

Without using "UIHint" how to create .cshtml for certain types of data.
Examples
These are already working!
[DataType(DataType.Url)]
public string Link { get; set; }
will be used
Url.cshtml
[DataType(DataType.MultilineText)]
public string Descrition { get; set; }
will be used
MultilineText.cshtml
These, I am in doubt about how
public IEnumerable<SelectListItem> TypesList { get; set; }
will be used
???????????.cshtml
public DateTime? DateUpdated { get; set; }
will be used
???????????.cshtml
public int? Order { get; set; }
will be used
???????????.cshtml
Questions
In short, do not want to be putting in my ViewModel attributes, like that would work exactly like "Url" and "Multiline"
What should be the file name .cshtml that will be used to int?, IEnumerable<xx> or IEnumerable<string>
Remembering that you can not create files with "<", ">" or "?"
By convention (if you don't specify any template name):
public IEnumerable<SelectListItem> TypesList { get; set; } will use SelectListItem.cshtml (not that since you have an IEnumerable<T> the editor template will be rendered for each element of the collection and will be called T.cshtml)
public DateTime? DateUpdated { get; set; } will use DateTime.cshtml
public int? Order { get; set; } will use Int32.cshtml
...
You could also specify a template name in the view:
#Html.EditorFor(x => x.TypesList, "MyTemplate.cshtml")
Assuming TypesList is an IEnumerable<T> then MyTemplate.cshtml will be strongly typed to IEnumerable<T> and not to T.