views:

255

answers:

0

I'm using the wonderful xVal library for setting up client side validation of rules defined server side in ASP.NET MVC.

In my implementation I need buddy classes since I'm extending classes already defined by entity framework. (I recognize that MVC2 may make all this moot, but I'm staying in MVC1 until 2 goes live).

The DataAnnotationsValidationRunner doesn't detect valid email addresses [DataType(DataType.EmailAddress)] (it was letting "blah" through as valid), so I decided to try out Castle Validator. I set up a runner and followed the instructions here: http://xval.codeplex.com/Thread/View.aspx?ThreadId=50161 including:

internal class CastleValidationRunner { private static readonly CachedValidationRegistry registry = new CachedValidationRegistry();

public static IList<ErrorInfo> GetErrors(object instance)
{
    var result = new List<ErrorInfo>();
    var runner = new ValidatorRunner(registry);
    if (!runner.IsValid(instance)) {
        var errorSummary = runner.GetErrorSummary(instance);
        result.AddRange(from prop in errorSummary.InvalidProperties
                        from err in errorSummary.GetErrorsForProperty(prop)
                        select new ErrorInfo(prop, err));
    }
    return result;
}

}

It worked great if I move the validation out of the buddy classes and into the main class. However, when I set the validation rule within the buddy class, it doesn't get triggered. Here is the business object class:

[MetadataType(typeof(NewUserMetadata))]
public class NewUser
{
    public string FirstName { get; set; }
    public string Email { get; set; }
}

public class NewUserMetadata
{
    [Required]
    public string FirstName { get; set; }

    [Required]
    [DataType(DataType.EmailAddress)]
    public string PrimaryEmail { get; set; }
{

Comparing the Castle runner with the DataAnnotations one makes it pretty clear that only the latter was set up to look at buddy attributes. Here it is, straight out of the xVal documentation:

public static class DataAnnotationsValidationRunner
{
    /// <summary>
    /// Runs each ValidationAttribute associated with a property on the supplied instance
    /// and returns an ErrorInfo relating to each validation failure. Caution: certain
    /// ValidationAttribute types claim to be valid even when they aren't - this runner
    /// would need to detect those special cases if you plan to rely on it. Fortunately, 
    /// other validation runners (e.g., for Castle Validation and NHibernate.Validate) 
    /// report all their errors correctly.
    /// </summary>
    public static IEnumerable<ErrorInfo> GetErrors(object instance)
    {
        var metadataAttrib = instance.GetType().GetCustomAttributes(typeof(MetadataTypeAttribute), true).OfType<MetadataTypeAttribute>().FirstOrDefault();
        var buddyClassOrModelClass = metadataAttrib != null ? metadataAttrib.MetadataClassType : instance.GetType();
        var buddyClassProperties = TypeDescriptor.GetProperties(buddyClassOrModelClass).Cast<PropertyDescriptor>();
        var modelClassProperties = TypeDescriptor.GetProperties(instance.GetType()).Cast<PropertyDescriptor>();

        return from buddyProp in buddyClassProperties
               join modelProp in modelClassProperties on buddyProp.Name equals modelProp.Name
               from attribute in buddyProp.Attributes.OfType<ValidationAttribute>()
               where !attribute.IsValid(modelProp.GetValue(instance))
               select new ErrorInfo(buddyProp.Name, attribute.FormatErrorMessage(string.Empty), instance);
    }

    public static IEnumerable<ErrorInfo> GetErrorList(object instance)
    {
        return GetErrors(instance).ToList();
    }
}

I don't understand how to modify the Castle runner to work like this one so that it will pick up buddy classes. Can someone help with a rewritten runner? Also, I'd be happy to try NHibernate if a sample is more readily available.

Without the improved runner, my client side validation seems to be ok, but I want it working server-side too so that my unit tests will work.

Thanks in advance.