Validation does not work when I use Validator.TryValidateObject

痞子三分冷 提交于 2019-11-27 20:50:26

I found the answer here: http://forums.silverlight.net/forums/p/149264/377212.aspx

MVC recognizes the MetaDataType attribute, but other projects do not. Before validating, you need to manually register the metadata class:

TypeDescriptor.AddProviderTransparent(
            new AssociatedMetadataTypeTypeDescriptionProvider(typeof(Customer), typeof(CustomerMetadata)), typeof(Customer));

var isValid = Validator.TryValidateObject(new Customer(), context, results, true);

After some research I couldn't find any reason why TryValidateObject always return true if I use MetadataType (buddy class). But it works with the following code (xVal).

    public static IEnumerable<ErrorInfo> GetErrors(object instance, string name)
    {
        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>();

        var list = 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(modelProp.Name),
                       instance);

        if (name != null)
            list = list.Where(x => x.PropertyName == name);

        return list;
    }

Although I did not test your code in .NET 4.0, in .NET 3.5 / Silverlight 3, your metadata class should look like this:

[MetadataType(typeof(Customer.CustomerMetaData))]
public partial class Customer 
{ 
    internal sealed class CustomerMetaData 
    {
        private CustomerMetaData()
        {
        }

        [Required(ErrorMessage = "You must supply a name for a customer.")]        
        public string Name; 
    } 
}

There is an issue where the MetadataType attribute is not being recognized by the object context. While you can manually add the type descriptor before validation: TypeDescriptor.AddProviderTransparent( new AssociatedMetadataTypeTypeDescriptionProvider(typeof(Customer), typeof(CustomerMetaData)), typeof(Customer));

a more concise way to handle it would be to update the Entity Model .tt file, to add the following to each DTO:

    Type currentType = MethodBase.GetCurrentMethod().DeclaringType;
    object[] attributes = currentType.GetCustomAttributes(typeof(MetadataTypeAttribute),false);
    if(attributes.Length > 0)
    {
        //MetadataType attribute found!
        MetadataTypeAttribute metaDataAttribute = (MetadataTypeAttribute)attributes[0];
        TypeDescriptor.AddProviderTransparent(
            new AssociatedMetadataTypeTypeDescriptionProvider(
                currentType, metaDataAttribute.MetadataClassType),currentType);
    }

This will allow you to add the attributes to the partial classes:

[MetadataType(typeof(CustomerMetaData))]
public partial class Customer
{

}

public partial class CustomerMetaData
{
    [Required]
    public string CustomerName { get; set; }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!