Validation inside a class property - c#

The code is as follows:
public class MyEvent
{
public string EventTitle { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public bool IsInsideSameMonth
{
get
{
// No code is bug-free, so we assume this situation may occur somehow.
if (StartDate > EndDate)
{
// And when this occurs, we want to halt the application by throwing exception to prevent further potential damage.
throw new MyEventException("Start date is not supposed to be greater than end date.");
}
return (StartDate.Year == EndDate.Year && StartDate.Month == EndDate.Month);
}
}
public void Validate()
{
if (StartDate > EndDate)
{
throw new MyEventException("Start date is not supposed to be greater than end date.");
}
if (String.IsNullOrWhiteSpace(EventTitle))
{
throw new MyEventException("Title cannot be empty.");
}
}
}
public class MyEventException : Exception
{
public MyEventException(string message)
: base(message)
{
}
}
It might seem redundant that I perform StartDate > EndDate validation inside the IsInsideSameMonth property. I just prefer being on the safe side. But it feels as if I am doing something wrong but I cannot describe it.
Is this a good practice? Please share your valuable experience and thoughts.

You shouldn't use Auto-implemented property. You should instead use a backing private field and then check while setting the property like:
private DateTime _StartDate;
public DateTime StartDate
{
get { return _StartDate; }
set
{
if (value > _EndDate)
{
throw new MyEventException("Start date is not supposed to be greater than end date.");
}
else
{
_StartDate = value;
}
}
}
private DateTime _EndDate;
public DateTime EndDate
{
get { return _EndDate; }
set { _EndDate = value; }
}
Your current code would allow the user to set StartDate to any value and you will only realize if you check your property IsInsideSameMonth.

For the EventTitle you could declare a backing field and make the validation inside the setter of the property. Something like this:
string eventTitle;
public string EventTitle
{
get
{
return eventTitle;
}
set
{
if(!IsNullOrWhiteSpace(value))
eventTitle = value;
else
throw new MyEventException("Title cannot be empty.");
}
}
As for the DateTime objects you could follow the same path:
private DateTime startDate;
public DateTime StartDate
{
get
{
return startDate;
}
set
{
if (value > EndDate)
{
throw new MyEventException("Start date is not supposed to be greater than end date.");
}
else
{
startDate = value;
}
}
}
private DateTime endDate;
public DateTime EndDate
{
get
{
return endDate;
}
set
{
endDate = value;
}
}

It's better to control values when the properties are set, so you need to convert your automatic properties to old style (properties with private field) and put your checking there.

Here is the Microsoft property design guide.
Here is an example checking the values when setting, throwing if invalid. I've also defined functions for validation to reduce redundant code (in case you want to change your validation or error messaging- it will be in one location).
public class MyEvent
{
public bool IsInsideSameMonth
{
get
{
return (StartDate.Year == EndDate.Year && StartDate.Month == EndDate.Month);
}
}
private string _EventTile;
public string EventTitle
{
get { return _EventTile; }
set
{
ValidateTitle(value); // this will throw if invalid and leave the value of _EventTitle unchanged
_EventTile = value;
}
}
private DateTime _StartDate = DateTime.MinValue;;
public DateTime StartDate
{
get { return _StartDate; }
set
{
ValidateDates(value, EndDate); // this will throw if invalid and leave the value of _StartDate unchanged
_StartDate = value;
}
}
private DateTime _EndDate = DateTime.MaxValue;;
public DateTime EndDate
{
get { return _EndDate; }
set
{
ValidateDates(StartDate, value); // this will throw if invalid and leave the value of _EndDate unchanged
_EndDate = value;
}
}
private void ValidateDates(DateTime start, DateTime end)
{
if (start > end)
{
throw new MyEventException("Start date is not supposed to be greater than end date.");
}
}
private void ValidateTitle(string title)
{
if (String.IsNullOrWhiteSpace(title))
{
throw new MyEventException("Title cannot be empty.");
}
}
public void Validate()
{
ValidateDates(StartDate, EndDate);
ValidateTitle(EventTitle);
}
}
public class MyEventException : Exception
{
public MyEventException(string message)
: base(message)
{
}
}

Related

comparing start date with in the same list using C#

I have class called LaborRate as below. It has Id, start date, end date column.
user can see existing records from database table in a grid and he can edit them and/or he can add new records to the grid.
while saving existing modified records or new records I need to do below validation.
If the Start Date or End Date falls within Start Date & End Date on other record for then I need to through some error. I cannot depend on Id column because for newly added record the id value is -1. What is the best method to do this comparison.
public class LaborRate
{
private int _id;
private decimal _laborRate;
private DateTime _StartDate;
private DateTime _EndDate;
public int Id
{ get { return _id; } set { _id = value; } }
public Decimal Rate
{ get { return _laborRate; } set { _laborRate = value; } }
public DateTime StartDate
{ get { return _StartDate; } set { _StartDate = value; } }
public DateTime EndDate
{ get { return _EndDate; } set { _EndDate = value; } }
}

Setting a dateTime property to null

I have this class which is a nuget package with a nullable dateTime property. This property is set depending on when another property is filled. Now, I do not want this property to have this property and I will like to set it null. Here is the class in the private nuget package
public class myClass
{
private string _todaysDate;
public DateTime? todaysDateAsDateTime
{
get
{
DateTime? result;
return !this.todaysDate.TryParseToDate(out result) ? new DateTime?() : result;
}
set => this.todaysDate = value.ParseToDate();
}
public string todaysDate
{
get => this._todaysDate;
set => this._todaysDate= value;
}
}
ParseToDate and TryParseToDate are extension menthods in thesame nuget
public static string ParseToDate(this DateTime? value) => value?.ToString("yyyy-MM-dd", (IFormatProvider) CultureInfo.InvariantCulture);
public static bool TryParseToDate(this string value, out DateTime? result)
{
if (string.IsNullOrEmpty(value))
{
result = new DateTime?();
return true;
}
bool flag = false;
DateTime result1 = DateTime.UtcNow;
if (value.Length == 10)
flag = DateTime.TryParseExact(value, "yyyy-MM-dd", (IFormatProvider) CultureInfo.InvariantCulture, DateTimeStyles.None, out result1);
if (flag)
{
result = new DateTime?(DateTime.SpecifyKind(result1, DateTimeKind.Utc));
return true;
}
result = new DateTime?();
return false;
}
when using this class, is there a possibility of setting todaysDateAsDateTime to null? because I do not want to have this value. The todaysDate is filled by the program when I use it in my application
In the property todaysDateAsDateTime, you need manage case in get/set when the value is null :
public class myClass
{
private string _todaysDate;
public DateTime? todaysDateAsDateTime
{
get =>
this._todaysDate != null && this._todaysDate.TryParseToDate(out DateTime? result) ?
result : null;
set => this._todaysDate = value?.ParseToDate();
}
public string todaysDate
{
get => this._todaysDate;
set => this._todaysDate = value;
}
}
I think you begin with C#, I add the same code without sugar syntax :
public class myClass
{
private string _todaysDate;
public DateTime? todaysDateAsDateTime
{
get
{
DateTime? result;
if(this._todaysDate!=null && this.todaysDate.TryParseToDate(out result))
{
return result;
}
else
{
null;
}
}
set
{
if(value != null)
{
this._todaysDate = value.ParseToDate();
}
else
{
this._todaysDate = null;
}
}
}
public string todaysDate
{
get => this._todaysDate;
set => this._todaysDate = value;
}
}

Validate Json data while calling DeserializeObject<Object>( ... )

I want to validate Json code after I deserialize it.
For example if I have ...
using Newtonsoft.Json;
...
public Car
{
public int Year{ get; set; }
public String Make{ get; set; }
}
...
JsonConvert.DeserializeObject<Car>(json)
I want to validate that the year is < 2017 && >=1900, (for example).
Or maybe make sure that the Make is a non empty string, (or it is an acceptable value).
I know I could add a Validate() type function after I deserialize, but I am curious if there was a way of doing it at the same time as the JsonConvert.DeserializeObject<Car>(json)
Probably the right tool for the job is a serialization callback
Just create a Validate method and slap on it an [OnDeserialized] attribute:
public Car
{
public int Year{ get; set; }
public String Make{ get; set; }
[OnDeserialized]
internal void OnDeserializedMethod(StreamingContext context)
{
if (Year > 2017 || Year < 1900)
throw new InvalidOperationException("...or something else");
}
}
Plug it in with the Setters.
public class Car
{
private int _year;
public int Year
{
get { return _year; }
set
{
if (_year > 2017 || _year < 1900)
throw new Exception("Illegal year");
_year = value;
}
}
}
For entire object validation, just validate anytime a value is set.
public class Car
{
private int _year;
private string _make;
public string Make
{
get { return _make; }
set
{
_make = value;
ValidateIfAllValuesSet();
}
}
public int Year
{
get { return _year; }
set
{
_year = value;
ValidateIfAllValuesSet();
}
}
private void ValidateIfAllValuesSet()
{
if (_year == default(int) || _make == default(string))
return;
if (_year > 2017 || _year < 1900)
throw new Exception("Illegal year");
}
}

Return a null from Function which returns a DateTime

public static DateTime ResolveDate()
{
return null;
}
Im required to return a null value from a function which returns a DateTime Type .. My Main Objective is to NOT to use MinValue or return a new Datetime(); [Which Returns a Default Date] and Frontend display the Date as a Empty Value (Blank) -> ""
public static DateTime? ResolveDate()
{
if ( someconditon = true )
{
return DateTime.Now
}
else
{
return null;
}
}
An alternative is to use something like TryParse is working
Public static bool TryResolve (out Resolvedate)
{
if ( someconditon = true )
{
Resolvedate = DateTime.Now
return true;
}
else
{
return false;
}
}
Make it nullable
public static DateTime? ResolveDate()
{
return null;
}
You can then return null as you want and check accordingly
Have a read of this Nullable Types for more information.
You can either return a Nullable<DateTime> like so:
public static DateTime? ResolveDate()
{
if (notResolvable)
{
return null;
}
}
Which would be useable like so:
var date = ResolveDate();
if (date.HasValue)
{
// Use date.Value here
}
Or use the Try naming convention like so:
public static bool TryResolveDate(out DateTime date)
{
date = default(DateTime);
if (notResolvable)
{
return false;
}
}
Which would be useable like so:
DateTime date;
if (TryResolveDate(out date))
{
// Use date here
}
bool somecondition = true;
public DateTime? ResolveDate()
{
DateTime? date = null;
if (somecondition == true)
{
date = DateTime.Now;
return DateTime.Now;
}
else return date;
}

How to do a custom validator that tests if one of 2 datetime properties is larger than another in asp.net mvc3

I have this class called DateTimeViewModelNullable where I have a method that returns a DateTime?
public DateTime? GetDateTime
{
get
{
DateTime date;
if (DateTime.TryParse(Date, CultureInfo.CurrentCulture, DateTimeStyles.None, out date))
{
try
{
return new DateTime(date.Year, date.Month, date.Day, ParseInt(Hour), ParseInt(Minute), 0, DateTimeKind.Utc);
}
catch (ArgumentException)
{
return null;
}
}
return null;
}
}
I then have 2 properties of that type in a viewmodel
public DateTimeViewModelNullable DateOut { get; set; }
[CustomIsValidDate(DateOut.GetDateTime,DateIn.GetDateTime,ErrorMessage = "Returning date must be higher than ingoing date!")]
public DateTimeViewModelNullable DateIn { get; set; }
I need to ensure (And print for the user) if the startdate is greater than the enddate so I created the customvalidator, but dont work
public class CustomIsValidDate : ValidationAttribute
{
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public CustomIsValidDate(DateTime? startDate, DateTime? endDate)
{
StartDate = startDate;
EndDate = endDate;
}
public override bool IsValid(object value)
{
if (StartDate == null || EndDate == null)
return true;
if (EndDate > StartDate)
return true;
return false;
}
}
I get the error Error An object reference is required for the non-static field, method, or property
Just use two datetime? properties on your model and implement System.ComponentModel.DataAnnotations.IValidatableObject on this model as below:
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var validations = new List<ValidationResult>();
if (From.HasValue && Until.HasValue)
{
if (From.Value.Date < Until.Value.Date)
validations.Add(new ValidationResult("StartDateMustBeBeforeEndDate"));
else if (From.Value.Date == From.Value.Date
&& From.Value.Hour < Until.Value.Hour)
validations.Add(new ValidationResult("StartHourMustBeBeforeEndHour"));
else if (From.Value.Date == From.Value.Date
&& From.Value.Hour == Until.Value.Hour
&& From.Value.Minute < Until.Value.Minute)
validations.Add(new ValidationResult("StartMinuteMustBeBeforeEndMinute"));
}
return validations;
}
You are using static variable in GetDateTime method, you have to remove that.

Categories

Resources