Sunday, September 19, 2010

If the input is optional, make it obvious in code

public static class Helper
{
    public static string CheckOverlookedValidation(object obj, string propName)
    {
        return Helper.CheckOverlookedValidation(obj, propName, null);
    }


    public static string CheckOverlookedValidation(object obj, string propName, params string[] exceptPropNames)
    {

        if (exceptPropNames != null)
        {
            foreach(string epn in exceptPropNames)
            {
                if (epn == propName)
                    return null;
            }
        }

        Type t = obj.GetType();

        PropertyInfo pi = t.GetProperty(propName);

        bool isNullable = !pi.PropertyType.IsValueType;

        if (!isNullable)
            if (pi.PropertyType.IsGenericType 
                && pi.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                isNullable = true;
            
        if (isNullable)
        {  
            object value = t.InvokeMember(propName, System.Reflection.BindingFlags.GetProperty, null, obj, null);
                
            string programmerAlert = "{0} has no custom validation. Alert the Programmer";

            if (value == null)
                return string.Format(programmerAlert, propName);
            else if (pi.PropertyType == typeof(string) && string.IsNullOrWhiteSpace((string)value))
                return string.Format(programmerAlert, propName);
            else
                return null;
        }

        return null;
    }
}




Assuming the user didn't input anything and by using the following code...

public class GuestResponse : IDataErrorInfo
{
    public string Name { get; set; }
    public string Email { get; set; }
    public string Phone { get; set; }
    public bool? WillAttend { get; set; }

    public string Error { get { return null; } }

    public string this[string propName]
    {
        get
        {
              
            if (propName == "Name" && string.IsNullOrEmpty(Name))
                return "Please enter your name";
            else if (propName == "Email" && (string.IsNullOrEmpty(Email) || !Regex.IsMatch(Email, ".+\\@.+\\..+")) )
                return "Please enter valid email address";
            else if (propName == "WillAttend" && !WillAttend.HasValue)
                return "Please specify whether you'll attend";
            else
                return Helper.CheckOverlookedValidation(this, propName);   
        }
    }

}

...the if (ModelState.IsValid) will output this:

  • Please enter your name
  • Please enter valid email address
  • Phone has no custom validation. Alert the Programmer
  • Please specify whether you'll attend

As we can see the Phone property in the Model(GuestResponse) being validated was caught by Helper.CheckOverlookedValidation. If we really want to make it obvious in code that the input is optional and not merely overlooked, do either this...

public string this[string propName]
{
    get
    {

        if (propName == "Name" && string.IsNullOrEmpty(Name))
            return "Please enter your name";
        else if (propName == "Email" && (string.IsNullOrEmpty(Email) || !Regex.IsMatch(Email, ".+\\@.+\\..+")))
            return "Please enter valid email address";
        else if (propName == "Phone")
            return null;
        else if (propName == "WillAttend" && !WillAttend.HasValue)
            return "Please specify whether you'll attend";
        else
            return Helper.CheckOverlookedValidation(this, propName);   
    }
}


...or this:

public string this[string propName]
{
    get
    {

        if (propName == "Name" && string.IsNullOrEmpty(Name))
            return "Please enter your name";
        else if (propName == "Email" && (string.IsNullOrEmpty(Email) || !Regex.IsMatch(Email, ".+\\@.+\\..+")))
            return "Please enter valid email address";
        else if (propName == "WillAttend" && !WillAttend.HasValue)
            return "Please specify whether you'll attend";
        else
            return Helper.CheckOverlookedValidation(this, propName, exceptPropNames : new[]{"Phone"});   
    }
}

Note, as there are no reliable way to check if value types(the non-nullable ones) has no value, we didn't assume in the code that for example, 0 is an overlooked input, we don't assume that logic in our code.

No comments:

Post a Comment