1

I have a class that is an entity from a database that has a bunch of dates represented as strings. For example, it could be something like this:

public class Stuff
{
    public string Date1 {get;set;}
    public string Date2 {get;set;}
    public string Date3 {get;set;}
    public string Date4 {get;set;}
}

I then have a Validation method that validates other properties and also validates the date properties. Currently, I am validating each date separately for each object. This works, but I was wondering if there was a way I could make it generic so I didn't have to duplicate code across classes and within the class itself. I am currently doing something like:

public bool ValidateDate(string date)
{
    string[] overrides = {"","__/__/____"};

    bool success = true;
    DateTime dateTime;

    if(!overrides.Contains(date) && !DateTime.TryParse(date,out dateTime))
    {
        success = false;
    }


    return success;
}

//Notice in this method I am repeating the if statements.
public bool Validate(Stuff stuff, out string message)
{
    message = string.Empty;
    bool success = true;

    if(!ValidateDate(stuff.Date1))
    {
        success = false;
        message = "Date 1 is invalid";
    }

    if(!ValidateDate(stuff.Date2))
    {
        success = false;
        message = "Date 2 is invalid";
    }

    if(!ValidateDate(stuff.Date3))
    {
        success = false;
        message = "Date 3 is invalid";
    }

    if(!ValidateDate(stuff.Date4))
    {
        success = false;
        message = "Date 4 is invalid";
    }

    return success;
}

void Main()
{
    string message;

    Stuff stuff = new Stuff();
    stuff.Date1 = "01/01/2020";
    stuff.Date2 = "__/__/____";
    stuff.Date3 = "";
    stuff.Date4 = "44/__/____";


    bool valid = Validate(stuff, out message);

}

I thought about doing something like:

public bool Validate<T>(T value, out string message)
{
    //Validation here
}

But, correct me if I am wrong, but this would require that I get the properties and use reflection to check the value of the date and my other problem with this is that the properties are strings, so there is no way for me to check if it is a DateTime?

Xaisoft
  • 45,655
  • 87
  • 279
  • 432
  • 2
    Don't re-invent the wheel, you should think to use 3rd party for validation like: Fluent validation or Data Annotation instead of doing manually – cuongle Jun 20 '13 at 04:09
  • @CuongLe - Can you point to a third party validation library that would do this, not only for dates, but other types as well? Were you referring to this: http://fluentvalidation.codeplex.com/ – Xaisoft Jun 20 '13 at 04:28
  • Yes, that's right ---- – cuongle Jun 20 '13 at 04:40
  • @CuongLe - Unfortunately for Fluent Validation, it requires .NET 4 and I am stuck with .NET 3.5. – Xaisoft Jun 20 '13 at 04:43
  • You can check this link: http://fluentvalidation.codeplex.com/discussions/350799 it is old version for .Net 3.5 SP 1. – Toan Vo Jun 20 '13 at 04:51

3 Answers3

1

I feel like I'm missing some information - right now the duplication that I see is that you are calling ValidateDate multiple times. I don't think there is a way around that - you have to call Validate on each Date property, unless (as you mentioned) you want to go the reflection route.

With reflection you would simply iterate through all properties and find any property who's name matches the pattern Date[number], you would then validate that it was indeed a DateTime (with Parse like you already do) and then move on.

If the number of fields is known and isn't too much I'd stick with your ValidateMethod though notice that message will currently only ever show you the last error (it gets overwritten each time).

You could get cute and write a method like:

public bool Validate(Stuff stuff, out string message)
{
    message = "Invalid Date(s): ";

    return ValidateDates(ref message, stuff.Date1, stuff.Date2, stuff.Date3, stuff.Date4);
}

public bool ValidateDate(ref string message, params string[] dates)
{
    bool rv = true;

    for (int i = 0; i < dates.Length; i++)
    {
        if (![validate DateTime as above])
        {
            message += i + " "; // add the failed index to the message
            rv = false;
        }
    }
    return rv;
}
Oren
  • 3,273
  • 2
  • 19
  • 15
  • Well, you are correct in that I would loop through all the properties, but a Date property doesn't necessarily have the word "Date" in its name, so I would actually have to hit the database and check a certain table that tells me if the property is a date and I would hate to have to do this. – Xaisoft Jun 20 '13 at 04:25
0

The best you can do is probably something like this, although I don't advocate using string to represent dates.

public bool ValidateDates(params string[] dates)
{
    return dates.All( d => ValidateDate(d));
}

public bool Validate(Stuff stuff, out string message)
{

    ValidateDates(stuff.Date1,stuff.Date2,stuff.Date3);
}

You can obviously play around with this to know which date failed, for instance you can do something like

var item = dates.Select( d, i => new 
{
   Valid = ValidateDate(d),
   Message = String.Format("Date {0} failed", i)
}).FirstOrDefault( x => !x.Valid);

if(item == null) //everything is valid
  return true;
else 
   //set the out param
   outMessageStr = item.Message;

return false;
Stan R.
  • 15,757
  • 4
  • 50
  • 58
  • Yes, I agree, storing dates as strings is not a good idea. This was a result of an old MySQL database designed for some hardware systems and now I am stuck with dates as strings. I am not familiar with the syntax starting from `var item = date.Select(d, i => .....`. Would you mind explaining it? – Xaisoft Jun 20 '13 at 04:27
  • @Xaisoft I am basically using LINQ to iterate through the collection of string dates (instead of a forloop), for each date I create an anonymous class which simply contains 2 properties, whether the date was valid and it's error message using "i" as the index, after that Select runs I should have a Enumerable of these anonymous object, after which I simply select the FIRST one that IS NOT valid...if they're all valid I'll simply have a NULL in my item variable. – Stan R. Jun 20 '13 at 04:36
0

You could make everything generic by using interfaces for the objects that you want to get dates from.

For instance:

//Defines an interface that provides a function to get multiple dates from an object.
public interface IGetDates
{
     //You could use a Read-Only property too
     string[] GetDates();
}

public class Stuff : IGetDate
{
     //other stuff...

     public string[] GetDates()
     {
          return new[]{ Date1, Date2, Date2, ect...};
     }
}

Then your generic method would look like this:

//Uses generic constraints so only objects that use the 
//IGetDates interface can call this method.
public bool Validate<T>(T stuff, out string message) where T : IGetDates
{
     message = string.Empty;
     bool success = true;
     string[] dates = stuff.GetDates();
     for(int i = 0; i < dates.Length; i++)
     {
          if(!Validate(dates[i]))
          {
              success = false;
              message = string.Format("Date {0} is invalid.", i);
          }
     }
     return success;
}
AtinSkrita
  • 1,373
  • 12
  • 13
  • My dates are not `DateTime`. – Xaisoft Jun 20 '13 at 04:32
  • They don't need to be, I changed it. – AtinSkrita Jun 20 '13 at 04:34
  • Can I implement an interface if `Stuff` is an entity? What I mean is that `Stuff` is not a POCO class for example, it is a generated entity class based on an edmx file. – Xaisoft Jun 20 '13 at 04:37
  • You can see [this](http://stackoverflow.com/a/5044650/1832856) answer for that. As he says, generated entity framework classes are partial, therefore you can extend whatever you need in another file. – AtinSkrita Jun 20 '13 at 04:43