HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Validating the value of a property of an object

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
thevaluevalidatingpropertyobject

Problem

This code snippet is intended for validating the value of a property of an object. The min and max range is supplied as an .xml file like Nhibernate .hbm files. Since the data-type of the property is also read from the .xml file, we can only know the type of the property at run-time.

Is there any better way of improving the code in C# and .NET 2.0?

```
public static void ValidateMinMax(Property prop, Object value)
{
Type type = Type.GetType(prop.TypeName);

Object minValue = PropertyDataExtractor.GetMinValue(prop);
Object maxValue = PropertyDataExtractor.GetMaxValue(prop);
Object actualVaue = null;

bool minValueOk = false;
bool maxValueOk = false;

if (minValue != null)
{
switch (type.Name)
{
case "Boolean":
break;

case "SByte":
actualVaue = Convert.ToSByte(value);
if (actualVaue != null)
{
minValueOk = ((sbyte)minValue) = ((sbyte)actualVaue);
}
else
{
maxValue = true;
}
break;

case "Byte":
actualVaue = Convert.ToByte(value);
if (actualVaue != null)
{
maxValueOk = ((byte)minValue) >= ((byte)actualVaue);
}
else
{
maxValue = true;
}
break;

case "Byte[]":
break;

case "DateTime":
actualVaue = Convert.ToDateTime(value);
if (actualVaue != null)
{
maxValueOk = ((DateTime)maxValue).Date >= ((DateTime)actualVaue).Date;
}

Solution

Here's an attempt to use a bit of polymorphism. While doing this some things I did note were:

1) The if statement at the end means that !minValueOk and !maxValueOk are irrelevant and will never be true. The first if should be !minValueOk and !maxValueOk rather than !(minValueOk && maxValueOk.

2) Assuming value is never null for some of the checks you do not need to double check for null. For example Convert.ToSByte(value) should always return a non-null value assuming it will convert.

3) I typically try not to through Exception where I can. Perhaps a custom exception might be worthwhile here.

Here's my attempt at a refactor. I'm not 100% sure it will compile but it should at least give an idea:

The new Validate method:

public static void ValidateMinMax(object prop, Object value)
{
    PropertyValidator validator = new PropertyValidatorFactory().CreateValidator(prop.GetType().Name);

    if (!validator.Validate(prop, value))
    {
        if (!isMinValid && !isMaxValid)
        {
             message = PropertyDataExtractor.GetMinValueErrorMessage(prop) + " " + PropertyDataExtractor.GetMaxValueErrorMessage(prop);
        }
        else if (!isMinValid)
        {
             message = PropertyDataExtractor.GetMinValueErrorMessage(prop);
        }
        else
        {
             message = PropertyDataExtractor.GetMaxValueErrorMessage(prop);
        }

        return InvalidPropertyRangeException("Property : " + PropertyDataExtractor.GetName(prop) + ".\nMessage : " + message);
    }
}


Using a custom exception and a Factory class to create the objects that will do the validation

class InvalidPropertyRangeException : Exception
{
    public InvalidPropertyRangeException(string message) : base(message)
    {
    }
}

class PropertyValidatorFactory
{
    public PropertyValidator CreateValidator(string typeName)
    {
        switch (typeName)
        {
            case "Boolean":
                return new EmptyValidator();
            case "String":
                return new StringValidator();
            // Rest of validators go here
            default:
                throw new NotImplementedException(string.Format("Type {0} is not supported", typeName));
        }
    }
}


The base validator class

abstract class PropertyValidator
    {
    public bool IsMinValid { get; private set; }
    public bool IsMaxValid { get; private set; }

    protected Object OriginalValue { get; private set; }

    protected PropertyValidator()
    {
        IsMinValid = IsMaxValid = true;
    }

    public bool Validate(object prop, Object value)
    {
        if(value == null)
        {
            throw new NullReferenceException("Value supplied for validation is null");
        }

        Object minValue = PropertyDataExtractor.GetMinValue(prop);
        Object maxValue = PropertyDataExtractor.GetMaxValue(prop);

        if(minValue == null && maxValue == null)
        {
            return true;
        }

        OriginalValue = value;

        return IsValid(minValue, maxValue);
    }

    private bool IsValid(object minValue, object maxValue)
    {
        IsMinValid = ValidateMinimum(minValue);
        IsMaxValid = ValidateMaximum(maxValue);

        return IsMinValid && IsMaxValid;
    }

    protected abstract bool ValidateMinimum(object minValue);
    protected abstract bool ValidateMaximum(object maxValue);
}


Examples of specific validator classes

class EmptyValidator : PropertyValidator
{
    protected override bool ValidateMinimum(object minValue)
    {
        return true;
    }

    protected override bool ValidateMaximum(object maxValue)
    {
        return true;
    }
}

class SbyteValidator : PropertyValidator
{
    protected override bool ValidateMinimum(object minValue)
    {
        sbyte actualVaue = GetValue();

        return ((sbyte)minValue) = actualVaue;
    }

    private sbyte GetValue()
    {
        return Convert.ToSByte(OriginalValue);
    }
}

class StringValidator : PropertyValidator
{
    protected override bool ValidateMinimum(object minValue)
    {
        string actualVaue = GetValue();
        if (actualVaue != null)
        {
            return ((int)minValue) = actualVaue.Length;
        }

        return false;
    }

    private string GetValue()
    {
        return Convert.ToString(OriginalValue);
    }
}

Code Snippets

public static void ValidateMinMax(object prop, Object value)
{
    PropertyValidator validator = new PropertyValidatorFactory().CreateValidator(prop.GetType().Name);

    if (!validator.Validate(prop, value))
    {
        if (!isMinValid && !isMaxValid)
        {
             message = PropertyDataExtractor.GetMinValueErrorMessage(prop) + " " + PropertyDataExtractor.GetMaxValueErrorMessage(prop);
        }
        else if (!isMinValid)
        {
             message = PropertyDataExtractor.GetMinValueErrorMessage(prop);
        }
        else
        {
             message = PropertyDataExtractor.GetMaxValueErrorMessage(prop);
        }

        return InvalidPropertyRangeException("Property : " + PropertyDataExtractor.GetName(prop) + ".\nMessage : " + message);
    }
}
class InvalidPropertyRangeException : Exception
{
    public InvalidPropertyRangeException(string message) : base(message)
    {
    }
}

class PropertyValidatorFactory
{
    public PropertyValidator CreateValidator(string typeName)
    {
        switch (typeName)
        {
            case "Boolean":
                return new EmptyValidator();
            case "String":
                return new StringValidator();
            // Rest of validators go here
            default:
                throw new NotImplementedException(string.Format("Type {0} is not supported", typeName));
        }
    }
}
abstract class PropertyValidator
    {
    public bool IsMinValid { get; private set; }
    public bool IsMaxValid { get; private set; }

    protected Object OriginalValue { get; private set; }

    protected PropertyValidator()
    {
        IsMinValid = IsMaxValid = true;
    }

    public bool Validate(object prop, Object value)
    {
        if(value == null)
        {
            throw new NullReferenceException("Value supplied for validation is null");
        }

        Object minValue = PropertyDataExtractor.GetMinValue(prop);
        Object maxValue = PropertyDataExtractor.GetMaxValue(prop);

        if(minValue == null && maxValue == null)
        {
            return true;
        }

        OriginalValue = value;

        return IsValid(minValue, maxValue);
    }

    private bool IsValid(object minValue, object maxValue)
    {
        IsMinValid = ValidateMinimum(minValue);
        IsMaxValid = ValidateMaximum(maxValue);

        return IsMinValid && IsMaxValid;
    }

    protected abstract bool ValidateMinimum(object minValue);
    protected abstract bool ValidateMaximum(object maxValue);
}
class EmptyValidator : PropertyValidator
{
    protected override bool ValidateMinimum(object minValue)
    {
        return true;
    }

    protected override bool ValidateMaximum(object maxValue)
    {
        return true;
    }
}

class SbyteValidator : PropertyValidator
{
    protected override bool ValidateMinimum(object minValue)
    {
        sbyte actualVaue = GetValue();

        return ((sbyte)minValue) <= actualVaue;            
    }

    protected override bool ValidateMaximum(object maxValue)
    {
        sbyte actualVaue = GetValue();

        return ((sbyte)maxValue) >= actualVaue;
    }

    private sbyte GetValue()
    {
        return Convert.ToSByte(OriginalValue);
    }
}

class StringValidator : PropertyValidator
{
    protected override bool ValidateMinimum(object minValue)
    {
        string actualVaue = GetValue();
        if (actualVaue != null)
        {
            return ((int)minValue) <= actualVaue.Length;
        }

        return false;

    }

    protected override bool ValidateMaximum(object maxValue)
    {
        string actualVaue = GetValue();
        if (actualVaue != null)
        {
            return ((int)maxValue) >= actualVaue.Length;
        }

        return false;
    }

    private string GetValue()
    {
        return Convert.ToString(OriginalValue);
    }
}

Context

StackExchange Code Review Q#18175, answer score: 3

Revisions (0)

No revisions yet.