Лучшие практики валидации для модели и ViewModel

У меня есть отдельные классы модели и модели просмотра. Если классы модели просмотра выполняют только проверку уровня пользовательского интерфейса (см. Проверка: модель или модель просмотра ).

Я могу проверить действие post в контроллере, что модель (vewmodel) действительна.

Вопрос: как проверить модель (основной объект с аннотациями к данным).

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

//Model Class
public class User
{
    [Required]
    public string Email {get; set;}

    [Required]
    public DateTime Created {get; set;}
}

//ViewModel Class
public class UserViewModel
{
    [Required]
    public string Email {get; set;}

    [Required]
    public string LivesIn {get; set;}
}

//Post action
public ActionResult(UserViewModel uvm)
{
    if( ModelState.IsValid)
        //means user entered data correctly and is validated

    User u = new User() {Email = uvm.Email, Created = DateTime.Now};
    //How do I validate "u"?

    return View();
}

Должно получиться примерно так:

var results = new List<ValidationResult>();
var context = new ValidationContext(u, null, null);
var r = Validator.TryValidateObject(u, context, results);

Я думаю о добавлении этого метода проверки в базовый класс (бизнес-объекта) и проверке его, когда я сопоставляю класс модели представления с бизнес-объектом.

Какие-либо предложения?


person Yahya    schedule 24.06.2011    source источник


Ответы (2)


1) Используйте плавную проверку модели, которая получает информацию от пользователя. он более гибкий, чем аннотация данных, и его легче тестировать.

2) Возможно, вы захотите изучить automapper, используя automapper, вам не нужно писать x.name = y.name.

3) Для вашей модели базы данных я бы придерживался аннотаций данных.

Все нижеприведенное основано на новой информации

Прежде всего, вы должны разместить валидацию в обоих местах, как вы это сделали сейчас для фактической валидации модели, вот как я бы это сделал. Отказ от ответственности: это не лучший способ

Сначала обновите UserViewModel до

public class UserViewModel
    {
        [Required()]
        [RegularExpression(@"^(([A-Za-z0-9]+_+)|([A-Za-z0-9]+\-+)|([A-Za-z0-9]+\.+)|([A-Za-z0-9]+\++))*[A-Za-z0-9]+@((\w+\-+)|(\w+\.))*\w{1,63}\.[a-zA-Z]{2,6}$")]
        public String Email { get; set; }
    }

Затем обновите метод действия до

        // Post action
        [HttpPost]
        public ActionResult register (UserViewModel uvm)
        {
            // This validates the UserViewModel
            if (ModelState.IsValid)
            {

                try
                {
                    // You should delegate this task to a service but to keep it simple we do it here
                    User u = new User() { Email = uvm.Email, Created = DateTime.Now };
                    RedirectToAction("Index"); // On success you go to other page right?
                }
                catch (Exception x)
                {
                    ModelState.AddModelError("RegistrationError", x); // Replace x with your error message
                }

            }       

            // Return your UserViewModel to the view if something happened               
            return View(uvm);
        }

Теперь для пользовательской модели это становится сложным, и у вас есть много возможных решений. Решение, которое я придумал (вероятно, не лучшее), следующее:

public class User
    {
        private string email;
        private DateTime created;

        public string Email
        {
            get
            {
                return email;
            }
            set
            {
                email = ValidateEmail(value);
            }
        }

        private string ValidateEmail(string value)
        {
            if (!validEmail(value))
                throw new NotSupportedException("Not a valid email address");     

            return value;
        }

        private bool validEmail(string value)
        {
            return Regex.IsMatch(value, @"^(([A-Za-z0-9]+_+)|([A-Za-z0-9]+\-+)|([A-Za-z0-9]+\.+)|([A-Za-z0-9]+\++))*[A-Za-z0-9]+@((\w+\-+)|(\w+\.))*\w{1,63}\.[a-zA-Z]{2,6}$");
        }

Последний модульный тест, чтобы проверить мой собственный код:

   [TestClass()]
    public class UserTest
    {

        /// <summary>
        /// If the email is valid it is stored in the private container
        /// </summary>
        [TestMethod()]
        public void UserEmailGetsValidated()
        {
            User x = new User();
            x.Email = "[email protected]";
            Assert.AreEqual("[email protected]", x.Email);
        }

        /// <summary>
        /// If the email is invalid it is not stored and an error is thrown in this application
        /// </summary>
        [TestMethod()]
        [ExpectedException(typeof(NotSupportedException))]
        public void UserEmailPropertyThrowsErrorWhenInvalidEmail()    
       {
           User x = new User();
           x.Email = "blah blah blah";
           Assert.AreNotEqual("blah blah blah", x.Email);
       }


        /// <summary>
        /// Clears an assumption that on object creation the email is validated when its set
        /// </summary>
        [TestMethod()]
        public void UserGetsValidatedOnConstructionOfObject()
        {
            User x = new User() { Email = "[email protected]" };
            x.Email = "[email protected]";
            Assert.AreEqual("[email protected]", x.Email);
        }
    }
person David    schedule 24.06.2011
comment
prd @Serghei Я действительно хотел знать, как мне проверить класс модели (который не привязан к просмотру). Сохранение того, что мои представления имеют свойства из разных классов модели (в классе ViewModel) для выполнения всех требований к этому конкретному представлению. - person Yahya; 27.06.2011
comment
@Yahya Можете опубликовать пример? будет легче указать, где и как проводить проверку. - person David; 27.06.2011
comment
prd Я добавил для вас образец кода в исходный вопрос. Надеюсь, теперь это имеет смысл. - person Yahya; 27.06.2011
comment
@Yahya, я обновил свой пост, указав возможное решение, но оно не самое лучшее. - person David; 27.06.2011
comment
prd Спасибо за решение. К сожалению, это выглядит не очень аккуратно. На данный момент, поскольку мои бизнес-объекты не являются критически важными, я просто живу без проверки на них. Выполнение всех проверок на моделях просмотра. Для отображения я последовал вашему совету и использую Automapper. - person Yahya; 29.06.2011

Я думаю, что лучше использовать аннотации к данным, посмотрите на это

ASP.NET MVC Проверка

а для проверки на стороне сервера вы можете использовать свободную проверку

и посмотрите на это вопрос

person Serghei    schedule 24.06.2011