MetadataType problem

2020-01-29 01:07发布

I'm using VS2008 SP1, WCF Ria Service July 2009 CTP. I found out that MetadataType does not work in partial class mode, really don't know what I have missed out:

Work:-

public partial class Person
{
    private string _Name;

    [Required(AllowEmptyStrings=false, ErrorMessage="Name required entry")]
    [StringLength(3)]
    public string Name
    {
        set{_Name = value;}
        get{return _Name;}
    }
}

class Program
{
    static void Main(string[] args)
    {
        Person p = new Person { Name="123432" };
        List res = new List();
        Validator.TryValidateObject(p,new ValidationContext(p,null,null),
            res,true);
        if (res.Count > 0)
        {
            Console.WriteLine(res[0].ErrorMessage);
            Console.ReadLine();
        }
    }
}

Not Work

public partial class Person
{
    private string _Name;

    public string Name
    {
        set{_Name = value;}
        get{return _Name;}
    }
}

[MetadataType(typeof(PersonMetadata))]
public partial class Person
{
}


public partial class PersonMetadata
{
    [Required(AllowEmptyStrings=false, ErrorMessage="Name required entry")]
    [StringLength(3)]
    public string Name;
}

class Program
{
    static void Main(string[] args)
    {
        Person p = new Person { Name="123432" };
        List res = new List();
        Validator.TryValidateObject(p,new ValidationContext(p,null,null),
            res,true);
        if (res.Count > 0)
        {
            Console.WriteLine(res[0].ErrorMessage);
            Console.ReadLine();
        }
    }
}

3条回答
够拽才男人
2楼-- · 2020-01-29 01:32

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

Before validating, you need to manually register the metadata class:

TypeDescriptor.AddProviderTransparent(
            new AssociatedMetadataTypeTypeDescriptionProvider(typeof(Person), typeof(PersonMetadata)), typeof(Person));

        List<ValidationResult> res = new List<ValidationResult>();
        bool valid = Validator.TryValidateObject(p, new ValidationContext(p, null, null), res, true);

(Original answer follows)

The problem isn't specifically with your partial class, it's that Validator.TryValidateObject doesn't seem to recognize the MetaDataType attribute. I have the same problem - the built-in validation in MVC 2 recognizes the metadata class, but TryValidateObject doesn't.

See these: Validating DataAnnotations with Validator class Validation does not work when I use Validator.TryValidateObject

As a side note, I don't know if it's necessary, but all examples I've seen for metadata classes employ the default get/set on each property:

[Required(AllowEmptyStrings=false, ErrorMessage="Name required entry")]
[StringLength(3)]
public string Name { get; set; }
查看更多
相关推荐>>
3楼-- · 2020-01-29 01:39

If you are working with WPF and EF, this has always worked for me...

[MetadataType(typeof(Department.Metadata))]
public partial class Department : BaseModel
{
    static Department()
    {
        TypeDescriptor.AddProvider(new AssociatedMetadataTypeTypeDescriptionProvider(typeof(Department),typeof(Metadata)), typeof(Department));
    }
    private sealed class Metadata
    {
        [Required(AllowEmptyStrings = false, ErrorMessage = "Department Name is required.")]
        [StringLength(50, ErrorMessage = "Name must be between 3 and 50 characters.", MinimumLength = 3)]
        public string Name;

        [StringLength(250, ErrorMessage = "Name must be between 10 and 250 characters.", MinimumLength = 10)]
        public string Description;
    }
}

And the base class that makes it happen...

public abstract class BaseModel : IDataErrorInfo
{
    #region Validation
    string IDataErrorInfo.Error
    {
        get { return null; }
    }
    string IDataErrorInfo.this[string propertyName]
    {
        get
        {
            var propertyInfo = GetType().GetProperty(propertyName);
            var results = new List<ValidationResult>();
            var result = Validator.TryValidateProperty(propertyInfo.GetValue(this, null), new ValidationContext(this, null, null)
            {
                MemberName = propertyName
            }, results);

            if (result) return string.Empty;
            var validationResult = results.First();
            return validationResult.ErrorMessage;
        }
    }
    #endregion
}
查看更多
狗以群分
4楼-- · 2020-01-29 01:54

Many thanks to Jeremy Gruenwald for the answer above... I was completely stuck on this one.

I wanted to create a standard validation class based on this solution, but I didn't want to have to pass in the metadata class type because it just felt ugly.

To achieve this I created a static class which does a lookup on the custom attributes to get the metadata class type and then registers that class before returning the validation results.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;

namespace MyApp.Validation
{
    public static class EntityValidator
    {
        public static List<ValidationResult> Validate(object instance, bool validateAllProperties = true)
        {
            RegisterMetadataClass(instance);

            var validationContext = new ValidationContext(instance, null, null);
            var validationResults = new List<ValidationResult>();

            Validator.TryValidateObject(instance, validationContext, validationResults, validateAllProperties);

            return validationResults;
        }

        private static void RegisterMetadataClass(object instance)
        {
            var modelType = instance.GetType();
            var metadataType = GetMetadataType(modelType);

            if (metadataType != null) 
            {
                TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(modelType, metadataType), modelType);
            }
        }

        private static Type GetMetadataType(Type type)
        {
            var attribute = (MetadataTypeAttribute)type.GetCustomAttributes(typeof (MetadataTypeAttribute), true).FirstOrDefault();
            return attribute == null ? null : attribute.MetadataClassType;
        }
    }
}

usage is a simple as:

var errors = EntityValidator.Validate(myEntity);
查看更多
登录 后发表回答