1

Core Question:
I have a generic interface IValidatingAttribute<T>, which creates the contract bool IsValid(T value); The interface is implemented by a variety of Attributes, which all serve the purpose of determining if the current value of said Field or Property they decorate is valid per the interface spec that I'm dealing with. What I want to do is create a single validation method that will scan every field and property of the given model, and if that field or property has any attributes that implement IValidatingAttribute<T>, it should validate the value against each of those attributes. So, using reflection I have the sets of fields and properties, and within those sets I can get the list of attributes. How can I determine which attributes implement IValidatingAttribute and then call IsValid(T value)?

background:
I am working on a library project that will be used to develop a range of later projects against the interface for a common third party system. (BL Server, for those interested)

BL Server has a wide range of fairly arcane command structures that have varying validation requirements per command and parameter, and then it costs per transaction to call these commands, so one of the library requirements is to easily define the valdiation requirements at the model level to catch invalid commands before they are sent. It is also intended to aid in the development of later projects by allowing developers to catch invalid models without needing to set up the BL server connections.

Current Attempt:
Here's where I've gotten so far (IsValid is an extension method):

public interface IValidatingAttribute<T>
{
    bool IsValid(T value);
}

public static bool IsValid<TObject>(this TObject sourceObject) where TObject : class, new()
{
    var properties = typeof(TObject).GetProperties();
    foreach (var prop in properties)
    {
        var attributeData = prop.GetCustomAttributesData();
        foreach (var attribute in attributeData)
        {
            var attrType = attribute.AttributeType;
            var interfaces = attrType.GetInterfaces().Where(inf => inf.IsGenericType).ToList();
            if (interfaces.Any(infc => infc.Equals(typeof(IValidatingAttribute<>))))
            {
                var value = prop.GetValue(sourceObject);

                //At this point, I know that the current attribute implements 'IValidatingAttribute<>', but I don't know what T is in that implementation.
                //Also, I don't know what data type 'value' is, as it's currently boxed as an object.
                //The underlying type to value will match the expected T in IValidatingAttribute.
                //What I need is something like the line below:
                if (!(attribute as IValidatingAttribute<T>).IsValid(value as T)) //I know this condition doesn't work, but it's what I'm trying to do.
                {
                    return false;
                }
            }
        }
        return true;
    }
}

Example usage:
Just to better explain what I am trying to achieve:

public class SomeBLRequestObject
{
    /// <summary>
    /// Required, only allows exactly 2 alpha characters.
    /// </summary>
    [MinCharacterCount(2), MaxCharacterCount(2), IsRequired, AllowedCharacterSet(CharSets.Alpha))]
    public string StateCode {get; set;}
}

And then, later on in code:

...
var someBLObj = SomeBLRequestObjectFactory.Create();
if(!someBLObj.IsValid())
{
    throw new InvalidObjectException("someBLObj is invalid!");
}

Thank you, I'm really looking for a solution to the problem as it stands, but I'm more than willing to listen if somebody has a viable alternative approach.

I'm trying to go generic extension method with this because there are literally hundreds of the BL Server objects, and I'm going with attributes because each of these objects can have upper double digit numbers of properties, and it's going to make things much, much easier if the requirements for each object are backed in and nice and readable for the next developer to have to use this thing.

Edit Forgot to mention : This Question is the closest I've found, but what I really need are the contents of \\Do Something in TcKs's answer.

Community
  • 1
  • 1
J. Patton
  • 27
  • 10

1 Answers1

0

Well, after about 6 hours and a goods nights sleep, I realized that I was over-complicating this thing. Solved it with the following (ExtValidationInfo is the class that the below two extensions are in.):

Jon Skeet's answer over here pointed me at a better approach, although it still smells a bit, this one at least works.

    public static bool IsValid<TObject>(this TObject sourceObject) where TObject : class, new()
    {
        var baseValidationMethod = typeof(ExtValidationInfo).GetMethod("ValidateProperty", BindingFlags.Static | BindingFlags.Public);

        var properties = TypeDataHandler<TObject>.Properties;
        foreach (var prop in properties)
        {
            var attributes = prop.GetCustomAttributes(typeof(IValidatingAttribute<>)).ToList();

            if (!attributes.Any())
            {
                continue; // No validators, skip.
            }


            var propType = prop.PropertyType;
            var validationMethod = baseValidationMethod.MakeGenericMethod(propType);

            var propIsValid = validationMethod.Invoke(null, prop.GetValue(sourceObject), attributes);
            if(!propIsValid)
            {
                return false;
            }
        }
        return true;
    }

    public static bool ValidateProperty<TPropType>(TPropType value, List<IValidatingAttribute<TPropType>> validators)
    {
        foreach (var validator in validators)
        {
            if (!validator.IsValid(value))
            {
                return false;
            }
        }
        return true;
    }
Community
  • 1
  • 1
J. Patton
  • 27
  • 10