time validation using annotations - c#

I want to enter date time as string inside textbox. Actually there are two input fields
From time
To time
Both should be in format like 00:00 so I can enter
From time 00:00
To time 23:59
how can validate user input in asp.net mvc viewmodel using annotations so I can restrict users input from 00:00 as min value and 23:59 as max value?

You could create a custom attribute that you can decorate your property with.
namespace JensB.Tools.CustomAttributes
{
public class IsDateOk: ValidationAttribute
{
public override bool IsValid(object value)
{
if (value == null) return false;
if (value.GetType() != typeof(DateTime)) throw new InvalidOperationException("can only be used on DateTime properties.");
bool isValid = // do validation here
return isValid;
}
}
}
You can then decorate your property like this:
[IsDateOk(ErrorMessage="Dates must be .....")]
public property DateTime MyTime {get; set ;}
The nice part about doing this is that you basically dont need to code anything on the front end and just need to display the #Html.ValidationMessageFor( x=> ....) to make this work

Using regular expression, you can validate your model, and client side validation also work with this
like
[RegularExpression(#"^(0[1-9]|1[0-2]):[0-5][0-9] (am|pm|AM|PM)$", ErrorMessage = "Error message")]
public string FromTime {get;set;}

Related

ASP.NET MVC How to handle dynamic datetime format from database

In my ASP.NET MVC 5 dwith EF 6 project, I have a database where datetime format is stored as string like "dd-MM-yyyy". User can change this format any time. User will use the given format in the date fields in the view. But when they will post that. Automatically it will bind as a DateTime for that property. I am statically handling it by the following code
[DataType(DataType.Time), DisplayFormat(DataFormatString = "{HH:mm}", ApplyFormatInEditMode = true)]
public DateTime? EndingTime { get; set; }
public string EndingTimeValue
{
get
{
return EndingTime.HasValue ? EndingTime.Value.ToString("HH:mm") : string.Empty;
}
set
{
EndingTime = DateTime.Parse(value);
}
}
but I know it's not a best way to do that. There may need a model binder or filter or any kind of custom attribute. I will be greatly helped if you give me a efficient solution with sample code. Thanks in advance.
NB: I am using razor view engine. and my solution consists of 7 projects. So there is no chance of using Session in model. Again I have a base repository class for using entity framework.
People usually store the datetime in the database as a datetime.
Then wherever you do a translation from datetime to string that datetime can be displayed in a format that depends on the culture of the viewer.
By doing this you can quickly make a page with datetime formats that will format the datetimes nicely wherever you are.
change the culture you pass to the toString and the format changes.
please see this MSDN page for more info about it.
edit: (see comments below)
anywhere on server:
string WhatYouWant = yourTime.ToCustomFormat()
and create an extension method for the datetime that gets the format out of the database and returns a string in the correct format.
public static class MyExtensions
{
public static string ToCustomFormat(this DateTime yourTime)
{
// Get the following var out of the database
String format = "MM/dd/yyyy hh:mm:sszzz";
// Converts the local DateTime to a string
// using the custom format string and display.
String result = yourTime.ToString(format);
return result;
}
}
This will allow you to call it anywhere anytime on your server. You can't access the method client side in javascript. I hope this helps.
(To be honest I'm a new developer too and still have a lot to learn ^^)
I have tried many options regarding this problem. Now what I am doing is created an action filter to catch all the DateTime and nullable DateTime Fields. Here I am providing the binder.
public class DateTimeBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
DateTime date;
var displayFormat = SmartSession.DateTimeFormat;
if (DateTime.TryParseExact(value.AttemptedValue, displayFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out date))
{
return date;
}
else
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName,"Invalid Format");
}
return base.BindModel(controllerContext, bindingContext);
}
}
in views the code I am formatting the date using same date format.

How do I disable model validation on a single field in C# MVC?

I have a view model that looks vaguely like:
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
I have a custom ModelBinder to parse the fields in the form, and assign the values to them. Which works.
However, when an error occurs, my ModelState ends up with two errors in it. The first from my ModelBinder, and the second from (I guess) the default validation rules:
- Invalid start date selected <-- My custom error message.
- The value 'fgfdg' is not valid for Start Date. <-- I want this to go away
How do I turn the default validation off for a specific field, on the server side?
Edit: Before you ask, yes my ModelBinder is extending DefaultModelBinder, but obviously I need the other default model binding behaviour; it's just these fields I want a custom behavior for.
(Why don't I just use standard validation rules? Because this is a search form, and depending on if the 'custom date range' is selected, we either ignore the StartDate and EndDate, or parse and perform various checks on them. Specifically, it is an absolute requirement that if the date ranges are invalid (eg. 'fdafsfsf' for start date, but 'search by XXX' instead of 'search by date range' is selected, the form must submit successfully without error)
Code fragment:
[ModelBinderType(typeof(MyViewModel))]
public class MyViewModelBinder : DefaultModelBinder {
public override object BindModel(ControllerContext cc, ModelBindingContext bc) {
var model = new MyViewModel();
var searchType = cc.HttpContext.Request["SearchType"];
if (searchType == "CustomDateRange") {
// Do checks here, etc.
// ONLY if searchType == "CustomDateRange" should there be ANY validation on StartDate
bc.ModelState.AddModelError("StartDate", "Invalid start date; outside of invoice range");
}
// bc.ModelState["StartDate"].Errors.Clear(); <--- Clears my error, not the default one.
bc.ModelMetadata.Model = model;
return base.BindModel(cc, bc);
}
}
You should be able to call ModelState.Errors.Clear on the specific property. E.g:
if (someCondition) {
bindingContext.ModelState["StartDate"].Errors.Clear();
return base.BindModel(......
}
Clear the ModelState then call the DefaultModelBinder implementation..

mvc validate date/time is at least 1 minute in the future

I am still getting my hands around MVC.
I have seen several similar questions, some custom code and various methods but I have not found something that works for me.
I have a search model that fills an HTML table with results inside of a partial view. I have this in my search results model:
public DateTime? BeginDateTime { get; set; }
Which is set to DateTime.Now in the controller. The user can specify that date and time to run a task with the search results' data on the model's POST call.
What I would like to do is validate that the date/time the user defined is at least 1 minute in the future. If this can be done as a client-side validation it will be better, but I am open to options as long as it works.
View:
Begin update: #Html.TextBoxFor(o => o.BeginDateTime, new { id="txtBegin" })
Thanks.
Create a new Attribute:
public class FutureDateAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
return value != null && (DateTime)value > DateTime.Now;
}
}
Now in your model set this attribute:
[FutureDate(ErrorMessage="Date should be in the future.")]
public DateTime Deadline { get; set; }
This is another good way to check that the date selected is from the future.
public class FutureDate : ValidationAttribute
{
public override bool IsValid(object value)
{
DateTime dateTime;
var isValid = DateTime.TryParseExact(
//Getting the value from the user.
Convert.ToString(value),
//We want the user to enter date in this format.
"d mmm yyyy",
//It checks if the culture is us-en
CultureInfo.CurrentCulture,
//Mosh has no idea what this does.
DateTimeStyles.None,
//Output parameter.
out dateTime);
return (isValid && dateTime > DateTime.Now);
}
}

Unit Testing with ASP.NET MVC 3 + Data Annotations and property DateTime

I'm on a project with DataAnnotations and ASP.NET MVC3. We use this solution to test the validations of "Date Annotions": http://gcbyjm.blogspot.com.br/2011/02/how-to-unit-test-dataannotations.html
I have problems to test a property "DateTime" of "ViewModel".
public class AchievementVM
{
...
[Required(ErrorMessage = "The date field is required.")]
[DataType(DataType.DateTime, ErrorMessage = "Invalid date.")]
public DateTime Date { get; set; }
...
}
[TestMethod]
public void AchievementVMValidator_ShouldHaveErrorWhenDateIsInvalid()
{
// Arrange
var achievementVM = new AchievementVM() { Date = ???? };
// Act
var errors = ValidationBuddy.GetErrors(achievementVM) as List<ErrorInfo>;
// Assert
ErrorInfo error = errors.Find(delegate(ErrorInfo e) { return e.ErrorMessage == "The date field is required."; });
Assert.IsTrue(error != null);
}
My question is how to pass the value of this property to simulate the ModelBind ERROR. In both situations, textbox empty and invalid data.
Thanks a lot!
If I understand your question correctly I think you should be testing action method that gets called by the view where the field is located. Like that you can pass invalid or empty values to the action method replicating empty or invalid values.
Remember that your view-model should be a representation of your view. So if your view allows a DateTime to not be entered (empty text box) then I think your view-model should have a nullable DateTime.
public DateTime? MyProperty { get; set; }
That way, in your unit test you can test for a null DateTime.
On your domain model this should be different as you dont want your database to receive a null DateTime so the domain model property should not be nullable. you view-model validation should stop the null date getting passed to the domain model or something converts it to a DateTime the domain model can handle. Maybe, for example, a null DateTime gets converted to DateTime.Now if your business requirements match this.
I hope this helps.

How to make my custom data annotations work( it is in a list and each item in the list overrides the last validation test)

I am having problems with my custom validation in asp.net mvc 3.0
What I want to it to do.
Be set on a property (right now I only can figure out how to make it on the class)
Be smart enough to realize there are multiple instances being used.
Scenario
textbox ( id = "textbox1")
dropdownlist (id ="ddl1")
textbox (id = "textbox2")
4 dropdownlist (id = "ddl2")
the values in the dropdownlist are the same. ("Days" and "Minutes")
Now user types in textbox1 30 and chooses "days" in ddl1. He then types in textbox2 10000 and "days" in ddl2. First one is valid second one is invalid as there is only 365 days in a year.
Scenario 2
User types in texbox1 99 and chooses "minutes" in ddl1. This of course is invalid and should fail.
So there could be any combination of where they might choose a valid day and invalid minute time.
Or both could be valid
So I have this so far
My viewmodel
[ReminderFormat(ErrorMessage = "test")]
public class ReminderViewModel
{
public List<SelectListItem> DurationTypes { get; set; }
public DurationTypes SelectedDurationType { get; set; }
public string ReminderLength { get; set; }
}
This will be in a list of view models and generates the number of ones I need
List<ReminderViewModel> viewModel = new List<ReminderViewModel>()
// add ReminderviewModel to this collection
View
// do a for loop through the viewModel ( List<ReminderViewModel> )
// generate a textbox html box and drop down list for each one
Data annotation
// set attribute to class for now but I want it on a property but I can't
// figure out how to get SelectedDurationType and pass it in. if I have my attribute on ReminderLength
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class ReminderFormatAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
var reminderViewModel = (ReminderViewModel)value;
switch (reminderViewModel.SelectedDurationType)
{
case DurationTypes.Days:
int length = Convert.ToInt32(reminderViewModel.ReminderLength);
if (length > 30)
{
return false;
}
else
{
return true;
}
default:
throw new ArgumentOutOfRangeException();
}
}
Problem 1
So as you can see I have the annotation on the class and I rather have it on the ReminderLength property.
Problem 2
Right now I just have days at 30 so it's less to type(I will change it later). The problem I am finding right now is that if textbox1 has 31 in it this will fail my validation.
This is correct. But if I have textbox2 that has 1 as the value it will pass validation. This is also correct.
What is not correct is that it will override the first invalid validation. So now it thinks all validation passed and goes into my action method. When it should reject it and go back to the view and tell the user that textbox1 failed validation.
Here's what I would do for your validation attribute:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class ReminderFormatAttribute: ValidationAttribute, IClientValidatable
{
public string DurationProperty { get; set; }
public ReminderFormatAttribute(string durationProperty)
{
DurationProperty = durationProperty;
ErrorMessage = "{0} value doesn't work for selected duration";
}
public override string FormatErrorMessage(string propName)
{
return string.Format(ErrorMessage, propName);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var durationType = validationContext.ObjectType.GetProperty(DurationProperty).GetValue(validationContext.ObjectInstance, null);
var reminderLength = value;
// Do your switch statement on durationType here
// Here is a sample of the correct return values
switch (durationType)
{
case DurationTypes.Days:
if (reminderLength > 30)
{
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
else
{
return null;
}
}
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "reminderformat",
};
rule.ValidationParameters["durationproperty"] = DurationProperty;
yield return rule;
}
}
Now in your ViewModel you can annotate the ReminderLength property like so:
[ReminderFormat("SelectedDurationType")]
public string ReminderLength { get; set; }
This is how I usually do it when the validation depends on the value of another property. The GetClientValidationRules method is the server side piece you need to tie into the unobtrusive client validation via jquery validate.
Check out this link for another ValidationAttribute example: http://www.devtrends.co.uk/blog/the-complete-guide-to-validation-in-asp.net-mvc-3-part-2
The example on this site also goes into writing the necessary jQuery to tie in the client validation

Categories