Валидация распространяется на связанные объекты - PullRequest
3 голосов
/ 13 сентября 2010

У меня есть такая модель:

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

    [Required(ErrorMessage="Name cant be empty")]
    public string Name { get; set; }

    public Person Friend { get; set; }
}

Я хочу создать нового персонажа и создать форму с полями, используя строго типизированный HtmlHelper

  • ID
  • Имя
  • Идентификатор друга (раскрывающийся список с такими параметрами, как <option value="Friend.ID">Friend.Name</option>

alt text

При отправке формы мой контроллер принимаетОбъект Person (p), который связан с использованием связывателя модели по умолчанию. Просто чтобы быть в явном виде, связыватель модели выполняет следующие действия: Свойства ID и Name связаны должным образом. Для Friend установлено новое значениеPerson экземпляр, чей ID равен идентификатору человека, которого я выбрал в раскрывающемся списке. Значение Friend.Name равно null, поскольку я не указал его значение в форме.

Проблема: Я хочу, чтобы RequiredAttribute срабатывал в текстовом поле Name, если оно пустое - и это так. Проблема в том, что он также запускается в атрибуте имени Friend.Поэтому, когда я отправляю сообщения, заполняя все поля, я получаю, что ModelState недействителен и ошибка в том, что p.Friend.Name iтребуется.alt text

Как бы я решил эту проблему?Конечно, в этом случае я не хочу проверять свойства Friend. Я думал о:

  1. Использование ViewModels для моих представлений, что каким-то образомрешить мои проблемы.Я еще не пробовал этого, так как чувствую, что мне действительно не нужно для такой простой задачи
  2. Отправка идентификатора друга в качестве отдельного параметра friend_id и привязка свойства Friend вручную.Только атрибуты ID и Name публикуемого лица привязаны, и я вручную установил свойство Friend.Это включает получение Friend из моего репозитория с использованием friend_id, чтобы сделать его «реальным» объектом Person.
  3. Проверка ModelState и удаление ошибок, которые я знаю, не учитываются.Это просто неправильно и не масштабируемо (не забудьте сделать это, если я добавлю, например, SecondFriend свойство

. Мне кажется, вариант 2 - самый выполнимый, но в идеале я быэто не может быть автоматическим. Кроме того, я не могу использовать строго типизированный помощник, поскольку атрибут friend_id textbox 'name должен соответствовать имени параметра метода действия.

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

Edit

На данный момент решена проблема с использованием ViewModels с ID, Name и Friend_id в качестве его свойств и теми же атрибутами проверки, что и у модели Person. Затем я сопоставляю ID и Name значения для нового экземпляра Person. Затем свойство Friend устанавливается путем загрузки указанного друга из хранилища. Например, newPerson.Friend = repository.Get(viewModel.Friend_id)

Когда я получаю время, планирую присмотреться к AutoMapper сделатьэто "автоматически".

Ответы [ 2 ]

1 голос
/ 16 сентября 2010

Проблема в том, что оба Person и Friend относятся к одному типу, и у них есть обязательный атрибут для свойства Name.

Просто чтобы убедиться, что я понимаю этот вопрос

Вы предоставляете следующие значения:

  • p.Name
  • p.ID
  • p.Friend.ID

но не p.Friend.Name.

Решение

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

Таким образом, ваша проверка не прервется на p.Friend.Name.

1 голос
/ 14 сентября 2010

Проблема в том, что ключи словаря состояния модели имеют только имя свойства в качестве ключа.Так что в вашем случае есть два из них, которые имеют одинаковый ключ: Name на Person и Name на Friend.

Я всегда изо всех сил пытался заставить вещи, подобные этой.Было бы лучше, если бы имена ключей имели обозначения типа VarName.PropertyName.В этом случае это будет работать, так как у вас будет два разных:

  • p.Name
  • p.Friend.Name

Имея ClassName.PropertyNameне сработает, поскольку вы можете иметь два параметра действия одного типа, но с другим именем.

Но как решить эту проблему?Один из способов - создать собственный фильтр действий, который выполняет проверку модели и заполняет ошибки состояния модели (сначала очистив их все).Это также означает, что вам придется использовать не лямбда-версии методов расширения HTML.Поэтому вместо Html.TextBoxFor вам нужно будет использовать расширение Html.TextBox и предоставить свои пользовательские ключи.

Чтобы сделать вещи более общими на стороне представления, вы, конечно, могли бы написать свои собственные перегрузки расширения HTML (те, которые вы используете), который будет принимать дополнительный параметр имени параметра, например:

Html.TextBoxFor("p", model => model.Friend.Name);

Это будет генерировать

<input type="text" name="p.Friend.Name" id="p_Friend_Name" />

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

...