I have a metadata class for my Customer where I validate the PurchaseDate.
The first annotation (DataType) is for formatting the date in an EditorFor, to show just the date part.
The second annotation is a custom validation to verify that the value is a DateTime, including a custom error message.
My problem is that the first annotation will cancel out the errormessage of the second annotation.
Is it possible to combine these two using only data annotations? Or do I have to format the date in the EditorFor?
[MetadataType(typeof(Customer_Metadata))]
public partial class Customer { }
public class Customer_Metadata
{
[DataType(DataType.Date)]
[MyDate(ErrorMessage = "Invalid purchase date")]
public DateTime? PurchaseDate { get; set; }
}
The same problem occurs if I try to replace the [DataType(DataType.Date)]
with
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
I won't get my custom error message.
EDIT
My main goal is to have a custom error message while also only showing the date part in the rendered input field. Is it possible with only data annotations?
Here's the MyDate attribute:
public class MyDate : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
DateTime dt;
var test = DateTime.TryParse((value ?? string.Empty).ToString(), out dt);
if (test)
{
return ValidationResult.Success;
}
else
{
return new ValidationResult(ErrorMessage);
}
}
}
Related
THE CONTEXT
I have an MVC form that includes two date pickers, for which I have written a custom validation attribute to ensure that both selected dates are not in the past (>= today).
THE PROBLEM
The validation attribute works only for the first date but not for the second, because the value passed to the "value" object in the validation attribute is always reset to the value set in the form class (HomeForm.cs) constructor (HomeForm( )).
Example (VS + browser screenshot):
The picked date is the 10/08/2021 but the Object value for dateB is 16/08/2021.
issue example
THE CODE
This is my form (HomeForm.cs):
public class HomeForm
{
public HomeForm()
{
dateA = DateTime.Now.Date;
TimeSpan time = new TimeSpan(1, 0, 0, 0);
dateB = DateTime.Now.Date + time;
}
[Required(ErrorMessage = "You must select a departure date.")]
[DataType(DataType.Date)]
[DisplayName("Departure date:")]
[DateTimeRange]
public DateTime dateA { get; set; }
[Required(ErrorMessage = "You must select a return date.")]
[DataType(DataType.Date)]
[DisplayName("Return date:")]
[DateTimeRange]
public DateTime dateB { get; set; }
};
A custom Validation Attribute "DateTimeRange" is applied to dateA and dateB:
[AttributeUsage(AttributeTargets.Property)]
public class DateTimeRangeAttribute : ValidationAttribute
{
private readonly DateTime _today;
public DateTimeRangeAttribute()
{
_today = DateTime.Now.Date;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value is not DateTime)
return new ValidationResult("Date format is not valid.");
if (!IsValid((DateTime)value))
return new ValidationResult("Chosen date is in the past.");
if(value.ToString().Count() != 19)
return new ValidationResult("Not a valid date. Choose a different one.");
return ValidationResult.Success;
}
private bool IsValid(DateTime value)
{
value = value.Date;
return value >= _today;
}
}
In my web api project, I have model class RegisterModel, There is nullable date time, I want to validate input enter by user only when if user has enter the dob.
I'm using Json.Net Serializer
my model class
[Validator(typeof(RegisterModelValidator))]
public class RegisterModel
{
[JsonProperty("dob")]
public Nullable<DateTime> DOB { get; set; }
}
my validator
public class RegisterModelValidator : AbstractValidator<RegisterModel>
{
public RegisterModelValidator()
{
RuleFor(x => x.DOB).Must(BeAValidDate).WithMessage("Please enter valid date.");
}
private bool BeAValidDate(DateTime date)
{
if (date == default(DateTime))
return false;
return true;
}
private bool BeAValidDate(DateTime? date)
{
if (date == default(DateTime))
return false;
return true;
}
}
But when I pass value e.g: "dob":"123 APR 2015"
It ModelState.IsValid return false, But does not return validation message.
Validating DateTimes using fluent validation only does not seem to possible, as mentioned here. This answer seems to provide the most useful ways of actually validating invalid dates.
public ActionResult UpdateDateOfBirth(ModelClass model)
{
if (ModelState.IsValid)
{
// your logic
}
AddErrors(ModelState);
return View();
}
// in my case i have to be add in viewbag
private void AddErrors(ModelStateDictionary result)
{
ViewBag.ErrorMessages = result;
//foreach (KeyValuePair<string,System.Web.Mvc.ModelState> error in result)
//{
// //ModelState.AddModelError(error.Key,error.Value.Errors[0].ErrorMessage);
//}
}
// and you can add the validation message on
public class ModelClass
{
[StringLength(100, ErrorMessage = "Not a valid date of birth"]
[DataType(DataType.DateTime)]
[Display(Name = "Date of Birth")]
public string DateOfBirth{ get; set; }
}
I am trying to develop an application using MVC 5 and EF 6 code first approach.
I am new at software devolpment.
I would like to add this data annotation:
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
to a property named Birthdate from a ViewModels
An Example to illustrate:
public class StudentRegisterViewModel
{
public string LastName { get; set; }
public string FirstMidName { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime EnrollmentDate { get; set; }
}
The problem here is that the field EnrollmentDate does not recognize DisplayFormat annotations.
Can anyone help me, to handle it, please?
The [DisplayFormat] attribute is only used by DisplayFor and EditorFor. And not by the other helpers like TextBoxFor.
To format it further, you can create a css class and use jQuery to apply it:
$("EnrollmentDate").addClass("class-name")
To apply the format specified in [DisplayFormat] you need to access the value via Html.DisplayFor(). For example, in a view that would be:
#Html.DisplayFor(vm => vm.EnrollmentDate)
Write your own ValidationAttribute. To do this you override the IsValid() method with your own logic.
Add more validation if you wish:
public class MyAmazingValidationClass : ValidationAttribute
{
protected override bool IsValid(object value)
{
DateTime date;
bool parsed = DateTime.TryParse((string)value, out date);
//or maybe :
bool parsed = DateTime.ParseExact((string)value),"dd/MM/yyyy")
if(!parsed)
return false;
return true;
}
}
Then go to your awesome class and decorate it:
public string FirstMidName { get; set; }
[DataType(DataType.Date)]
[MyAmazingValidationClass(ErrorMessage="You made a big mistake right now!")]
public DateTime EnrollmentDate { get; set; }
Im having some problems utilizing RegularExpression attribute in a ASP.net MVC project.
It seems to work client side, it goes away when it fits, however then upon post action, the model state is checked for being valid, it ends up posting error, that it must follow the regular expression.
I have tried theese following:
^[0-9]{1,2}/[0-9]{1,2}/[0-9]{4} [0-9]{1,2}:[0-9]{1,2}$
^\d{1,2}/\d{1,2}/\d{4} \d{1,2}:\d{1,2}$
Essentially it must catch 14/12/2014 14:20 as input.
Any ideas? I'm lost.
Model:
[Required]
[Display(Name = "TimeDisplay", ResourceType = typeof(Resources.Models.Log))]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy HH:mm}")]
[RegularExpression(#"^[0-9]{1,2}/[0-9]{1,2}/[0-9]{4} [0-9]{1,2}:[0-9]{1,2}$")]
public DateTime Time { get; set; }
Controller Action:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Log log)
{
if (ModelState.IsValid)
{
db.Logs.Add(log);
db.SaveChanges();
TempData["Notification"] = AlertGenerator.GenerateAlert(AlertGenerator.Level.Success, "Success! Log Saved");
return RedirectToAction("Index");
}
return View(log);
}
As I know, MVC will use current CultureInfo (on server) to parse DateTime format, so you cannot directly binding "dd/MM/yyyy HH:mm" to your entity.
My solution is creating ViewModel then use DateTime.ParseExact(...) to parse the date:
Model:
[Display(Name = "TimeDisplay", ResourceType = typeof(Resources.Models.Log))]
[Required]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy HH:mm}")]
public DateTime Time { get; set; }
ViewModel
[Display(Name = "TimeDisplay", ResourceType = typeof(Resources.Models.Log))]
[Required]
public string Time { get; set; }
Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(LogViewModel logViewModel)
{
if (ModelState.IsValid)
{
// convert from ViewModel to Entity Model
Log log = new Log(logViewModel);
// parse the time
log.Time = DateTime.ParseExact(logViewModel.Time, "dd/MM/yyyy HH:mm", null);
db.Logs.Add(log);
db.SaveChanges();
TempData["Notification"] = AlertGenerator.GenerateAlert(AlertGenerator.Level.Success, "Success! Log Saved");
return RedirectToAction("Index");
}
return View(log);
}
I have a data validation class that checks whether the start date of a meeting is before the end date.
The model automatically passes in the date that requires validation, but i'm having a bit of difficulty passing the data that it needs to be validated against.
Here's my validation class
sealed public class StartLessThanEndAttribute : ValidationAttribute
{
public DateTime DateEnd { get; set; }
public override bool IsValid(object value)
{
DateTime end = DateEnd;
DateTime date = (DateTime)value;
return (date < end);
}
public override string FormatErrorMessage(string name)
{
return String.Format(CultureInfo.CurrentCulture, ErrorMessageString, name);
}
}
Here's the class that contains the data annotations
[StartLessThanEnd(ErrorMessage="Start Date must be before the end Date")]
public DateTime DateStart { get; set; }
And here's my controller
[HttpPost, Authorize]
public ActionResult Create(Pol_Event pol_Event)
{
ViewData["EventTypes"] = et.GetAllEventTypes().ToList();
StartLessThanEndAttribute startDateLessThanEnd = new StartLessThanEndAttribute();
startDateLessThanEnd.DateEnd = pol_Event.DateEnd;
if (TryUpdateModel(pol_Event))
{
pol_Event.Created_On = DateTime.Now;
pol_Event.Created_By = User.Identity.Name;
eventRepo.Add(pol_Event);
eventRepo.Save();
return RedirectToAction("Details", "Events", new { id = pol_Event.EventID });
}
return View(pol_Event);
}
Validation attributes that work with multiple properties should be applied to the model and not on individual properties:
[AttributeUsage(AttributeTargets.Class)]
public class StartLessThanEndAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
var model = (MyModel)value;
return model.StartDate < model.EndDate;
}
}
[StartLessThanEnd(ErrorMessage = "Start Date must be before the end Date")]
public class MyModel
{
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}