Где вы положили подтверждение в asp.net MVC 3? - PullRequest
5 голосов
/ 02 июля 2011

Одна из распространенных рекомендуемых практик в asp.net mvc заключается в том, что вы не должны отправлять свои бизнес-модели своим представлениям .. вместо этого вы должны создавать модели представления, специфичные для каждого представления.

готово, и вы вызываете метод ModelState.IsValid в своем контроллере, вы фактически проверяете действительность модели представления, но не бизнес-объекта.

Каков обычный подход к решению этой проблемы?

public class Person
{
public int ID {get; set;};

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

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

public virtual ICollection<Exam> Exams {get; set;}

}

public class PersonFormViewModel
{

public int ID {get; set;};    


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

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

}

Это именно то, что у меня есть сейчас, но я не уверен, должен ли атрибут [Обязательный] отображаться в обеих моделях или только в ViewModel или только в бизнес-модели.

Любые советы по этому вопросу приветствуются.

Дополнительные ссылки в поддержку моего утверждения о том, что всегда рекомендуется использовать модели представлений.

Как добавить проверку в мои классы POCO (шаблон)

http://blogs.msdn.com/b/simonince/archive/2010/01/26/view-models-in-asp-net-mvc.aspx

Ответы [ 5 ]

7 голосов
/ 02 июля 2011

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

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

И, вероятно, у вас будут бизнес-правила / правила домена, основанные не только на«form», так что вы должны сделать это либо в моделях домена (выполнить проверку после их сопоставления), либо с уровнем обслуживания.

Во всех наших моделях есть метод под названием «Validate», который мы вызываем в службах перед сохранением.Они выдают пользовательские исключения, если они не проходят проверку бизнеса, которая перехватывается контроллером и также добавляется в состояние модели.

Возможно, это не всеобщая чашка чая, но она соответствует.

Пример проверки бизнеса в соответствии с запросом:

Вот пример модели домена, которая у нас есть, которая представляет собой общее «сообщение» (вопрос, фотография, видео и т. Д.):

public abstract class Post
{
   // .. fields, properties, domain logic, etc

   public void Validate()
   {
      if (!this.GeospatialIdentity.IsValidForThisTypeOfPost())
         throw new DomainException(this, BusinessException.PostNotValidForThisSpatial.);
   }
}

Вы видите, я проверяю бизнес-правила и выкидываю пользовательские исключения.DomainException является нашей базой, и у нас есть много производных реализаций.У нас есть перечисление с именем BusinessException, которое содержит значения для всех наших исключений.Мы используем методы расширения в enum для предоставления сообщения об ошибке на основе ресурсов.

Это не просто поле в модели, которую я проверяю, например, «Все сообщения должны иметь тему», потому что, хотя это является частьюдомен, это проверка входных данных в первую очередь и, таким образом, обрабатывается посредством аннотаций данных в модели представления.

Теперь контроллер:

[HttpPost]
public ActionResult Create(QuestionViewModel viewModel)
{
   if (!ModelState.IsValid)
     return View(viewModel);

   try
   {
      // Map to ViewModel
      var model = Mapper.Map<QuestionViewModel,Question>(viewModel);

      // Save.
      postService.Save(model); // generic Save method, constraint: "where TPost: Post, new()".

      // Commit.
      unitOfWork.Commit();

      // P-R-G
      return RedirectToAction("Index", new { id = model.PostId });
   }
   catch (Exception exc) 
   {
      var typedExc = exc as DomainException;

      if (typedExc != null)
      {
         // Internationalised, user-friendly domain exception, so we can show
         ModelState.AddModelError("Error", typedExc.BusinessError.ToDescription());
      }
      else
      { 
         // Could be anything, e.g database exception - so show generic msg.
         ModelState.AddModelError("Error", "Sorry, an error occured saving the Post. Support has been notified. Please try again later.");
      }
   }

   return View(viewModel);
}

Итак, к тому времени, когда мы получимк методу «Сохранить» в сервисе модель прошла проверку 1032 *.Затем метод Save вызывает post.Validate(), вызывая бизнес-правила.

Если возникает исключение, контроллер перехватывает его и отображает сообщение.Если он проходит метод Save и возникает другая ошибка (90% времени, например, это Entity Framework), мы показываем общее сообщение об ошибке.

Как я уже сказал, не для всех, но это работаетхорошо для нашей команды.У нас есть четкое разделение представления и проверки домена, а также последовательный поток управления от необработанного HTTP POST к перенаправлению после успеха.

HTH

4 голосов
/ 02 июля 2011

Класс MetaData "приятель" - это как раз то, для чего он нужен.Проверка создается один раз, но может использоваться как для модели, так и для классов модели представления:

public class PersonMetaData
{
  [Required] 
  public string Name {get; set;}  

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

[MetadataType(typeof(PersonMetaData))]
public class Person
{
  public string Name {get; set;}  
  public string LastName {get; set;} 
}

[MetadataType(typeof(PersonMetaData))]
public class PersonFormViewModel 
{
  public string Name {get; set;}  
  public string LastName {get; set;} 
}
2 голосов
/ 03 августа 2011

Отличный ответ от RPM1984 и хороший пример кода.

Я по-прежнему считаю, что использование Variant 2 с самого начала является прагматическим балансом между производительностью и структурой, но вы всегда должны быть готовы перейти на Variant3 в некоторых случаях, поэтому я выступаю за сочетание двух подходов, где логично.Тем не менее, в паттернах и практиках рекомендуется всегда использовать вариант 3, так как это идеальное разделение интересов и т. Д., И это позволяет избежать использования двух подходов в одном решении, которое некоторым людям не нравится, и многие клиенты, с которыми я работаю, выбирают вариант 3 и работают с этимдля всех моделей.

Я думаю, что ключевым моментом является то, что сказал RPM1984 - повторное использование ваших бизнес-сущностей в ваших View Models полезно для повторного использования проверки, но имейте в виду, что часто вашей бизнес-логике нужно делатьдругая проверка также (например, проверка записи еще не существует).Если вы используете Вариант 3, это дает вам возможность сосредоточить валидацию модели представления исключительно на потребностях ваших представлений (за счет небольшого дополнительного усилия), но вам также всегда потребуется некоторая проверка бизнес-логики.

1 голос
/ 02 июля 2011

Учитывая, что вы всегда передаете view-модели в ваше представление, и ваши доменные модели не предоставляются конечному пользователю через представления, тогда я не вижу необходимости в проверке самой доменной модели. Например, если вы получаете PersonViewModel из представления через сообщение формы, вы преобразуете его в Person Model (возможно, через автоматическое преобразование и т. Д.), Только если PersonViewModel сам по себе является действительным. Поэтому я считаю, что проверки входных данных должны оставаться в моделях представления, потому что они являются теми, которые связаны с вводом.

0 голосов
/ 02 июля 2011

Как правило, ваша ViewModel будет содержать ссылку на вашу модель - ViewModel необходима только в том случае, если вам нужно отобразить дополнительную информацию в вашем представлении, которая недоступна в вашей модели, нет необходимости копировать уже имеющиеся данные.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...