Понимание ValidationContext в DataAnnotations

Я хочу использовать Validator.TryValidateValue(), но не понимаю механику. Скажем, у меня есть следующее:

public class User {
    [Required(AllowEmptyStrings = false)]
    [StringLength(6)]
    public string Name { get; set; }
}

и метод:

public void CreateUser(string name) {...}

Мой код проверки:

ValidationAttribute[] attrs = bit of reflection here to populate from User class
var ctx = new ValidationContext(name, null, null);
var errors = new List<ValidationResult>();
bool valid = Validator.TryValidateValue(name, ctx, errors, attrs);

Он работает нормально, пока значение name не будет null. Я получаю ArgumentNullException при создании экземпляра ValidationContext и не понимаю, почему. TryValidateValue() также требует непустого контекста. У меня есть значение и список атрибутов для проверки. Что это за ValidationContext для?

Ответы

Ответ 1

Единственное, что неверно в вашем коде, это объект экземпляра для вашего контекста проверки. Экземпляр не должен быть значением, которое проверяется. Для Validator.ValidateProperty, да, это должен быть объект, которому принадлежит свойство, но для Validator.ValidateValue достаточно "this".

Я написал класс помощника проверки, чтобы выполнить настройку; это позволяет мне передавать произвольные значения из любого места.

public class ValidationHelper
{
    private List<ValidationResult> m_validationResults = new List<ValidationResult>();
    private List<ValidationAttribute> m_validationAttributes = new List<ValidationAttribute>();

    public Tuple<bool, List<string>> ValidateDOB(DateTime? dob)
    {
        m_validationAttributes.Add(new CustomValidationAttribute(typeof(DateOfBirthValidator), "ValidateDateOfBirth"));
        bool result = Validator.TryValidateValue(dob, 
                             new ValidationContext(this, null, null), 
                             m_validationResults, 
                             m_validationAttributes);
        if (result)
        {
            return Tuple.Create(true, new List<string>());
        }
        List<string> errors = m_validationResults.Select(vr => vr.ErrorMessage).ToList();
        return Tuple.Create(false, errors);
    }
}

Если вы проверяете свойства, которые имеют атрибуты проверки свойства, это намного проще:

internal void Validate(T value)
{
    if (!m_Initializing && TrackChanges && !Entity.IsImmutable)
    {
        Validator.ValidateProperty(value, new ValidationContext(Entity, null, null) { MemberName = PropertyName ?? ModelName });
    }
}

"Entity" является свойством текущего класса, который ссылается на объект, который я хочу проверить. Это позволяет мне проверять свойства на других объектах. Если вы уже находитесь внутри объекта, снова будет "this".