Валидация модели

Основы валидации модели

Последнее обновление: 22.02.2022

Большую роль в приложении играет валидация модели или проверка вводимых данных на корректность. Например, у нас есть класс пользователя, в котором определено свойство для хранения возраста. И нам было бы нежелательно, чтобы пользователь вводил какое-либо отрицательное число или заведомо невозможный возвраст, например, миллион лет.

Например, пусть у нас есть проект консольного приложения, в котором есть клас User:

public class User
{
    public string Name { get; set; }
    public int Age { get; set; }

    public User(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

В программе мы можем проверять вводимые данные с помощью условных конструкций:

CreateUser("Tom", 37);
CreateUser("b", -4);
CreateUser("", 130);

void CreateUser(string name, int age)
{
    User user = new User(name, age);
    // проверяем корректность значения свойства Name
    // если его длина в диапазоне от 3 до 50, то оно корректно
    if (user.Name.Length >= 3 && user.Name.Length <= 50)
        Console.WriteLine($"Name: {user.Name}");
    else
        Console.WriteLine("Incorrect name!");

    // проверяем корректность значения свойства Age
    // если оно в диапазоне от 1 до 100, то оно корректно
    if (age >= 1 && age <= 100)
        Console.WriteLine($"Age: {user.Age}\n");
    else
        Console.WriteLine("Incorrect age!\n");

}
public class User
{
    public string Name { get; set; }
    public int Age { get; set; }

    public User(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

Здесь предполагается, что имя должно иметь больше 1 символа, а возраст должен быть в диапазоне от 1 до 100. Однако в классе может быть гораздо больше свойств, для которых надо осуществлять проверки. А это привет к тому, что увеличится значительно код программы за счет проверок. К тому же задача валидации данных довольно часто встречается в приложениях. Поэтому фреймворк .NET предлагает гораздо более удобный функционал в виде атрибутов из пространства имен System.ComponentModel.DataAnnotations.

Итак, изменим касс User следующим образом:

using System.ComponentModel.DataAnnotations;

public class User
{
    [Required]
    [StringLength(50, MinimumLength = 3)]
    public string Name { get; set; }

    [Range(1, 100)]
    public int Age { get; set; }

    public User(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

Все правила валидации модели в System.ComponentModel.DataAnnotations определяются в виде атрибутов. В данном случае используются три атрибута: классы RequiredAttribute, StringLengthAttribute и RangeAttribute. В коде необязательно использовать суффикс Attribute, поэтому он, как правило, отбрасывается. Атрибут Required требует обзательного наличия значения. Атрибут StringLength устанавливает максимальную и минимальную длину строки, а атрибут Range устанавливает диапазон приемлемых значений.

Теперь изменим код программы:

using System.ComponentModel.DataAnnotations;

CreateUser("Tom", 37);
CreateUser("b", -4);
CreateUser("", 130);

void CreateUser(string name, int age)
{
    User user = new User(name, age);
    var context = new ValidationContext(user);
    var results = new List<ValidationResult>();
    if (!Validator.TryValidateObject(user, context, results, true))
    {
        Console.WriteLine("Не удалось создать объект User");
        foreach (var error in results)
        {
            Console.WriteLine(error.ErrorMessage);
        }
        Console.WriteLine();
    }
    else
        Console.WriteLine($"Объект User успешно создан. Name: {user.Name}\n");
}

Здесь определен метод CreateUser, который принимает два значения и с их помощью создает объект User. В этом методе используются классы ValidationResult, Validator и ValidationContext, которые предоставляются пространством имен System.ComponentModel.DataAnnotations и которые управляют валидацией.

Вначале мы создаем контекст валидации - объект ValidationContext. В качестве первого параметра в конструктор этого класса передается валидируемый объект, то есть в данном случае объект User.

var context = new ValidationContext(user);

Собственно валидацию производит класс Validator и его метод TryValidateObject(). В этот метод передается валидируемый объект (в данном случае объект user), контекст валидации, список объектов ValidationResult и булевый параметр, который указывает, надо ли валидировать все свойства.

var results = new List<ValidationResult>();
if (!Validator.TryValidateObject(user, context, results, true))
{
    //.......
}

Если метод Validator.TryValidateObject() возвращает false, значит объект не проходит валидацию. Если модель не проходит валидацию, то список объектов ValidationResult оказывается заполенным. А каждый объект ValidationResult содержит информацию о возникшей ошибке. Класс ValidationResult имеет два ключевых свойства: MemberNames - список свойств, для которых возникла ошибка, и ErrorMessage - собственно сообщение об ошибке.

Для тестирования три раза вызываем метод CreateUser, передавая в него сначала корректные, а потом некорректные данные:

CreateUser("Tom", 37);
CreateUser("b", -4);
CreateUser("", 130);

И мы получим следующий консольный вывод:

Объект User успешно создан. Name: Tom

Не удалось создать объект User
The field Name must be a string with a minimum length of 3 and a maximum length of 50.
The field Age must be between 1 and 100.

Не удалось создать объект User
The Name field is required.
The field Age must be between 1 and 100.

В первом вызове метода CreateUser передаются корректные данные, поэтому никаких ошибок при валидации не возникнет.

А во втором вызове CreateUser валидация завершится неудачно, так как свойствам User переданы некорректные значения. Например, свойству Name передается значение "b", что не соответствует правилам атрибута [StringLength(50, MinimumLength = 3)]. Также значение свойства Age - -4 не соответствует правилам атрибута [Range(1, 100)]. Соответственно консоль отобразит ошибки для свойств Name и Age.

В третьем вызове CreateUser валидация также завершится неудачно - значение свойства Age - 130 также не соответствует правилам атрибута [Range(1, 100)]. Но, кроме того, свойству Name передается пустая строка - значение, которое не соответствует правилу атрибута [Required]. Данный атрибут требует обязательного наличия значения.

Если применяются типы record, то атрибуты валидации можно указать непосредственно перед определением свойства:

using System.ComponentModel.DataAnnotations;

public record class User(

    [property: Required]
    [property: StringLength(50, MinimumLength = 3)]
    string Name,

    [property: Range(1, 100)] int Age
);

В этом случае перед названием атрибута указывается оператор property:

Таким образом, вместо кучи условных конструкций для проверки значений свойств модели мы можем использовать один метод Validator.TryValidateObject(), а все правила валидации определить в виде атрибутов.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850