как заполнить данные в контроллере и избежать ошибок ModelState - PullRequest
1 голос
/ 13 марта 2011

Может быть, это не совсем то решение, которое мне нужно, но это то, что я хочу сделать:

У меня есть форма регистрации компании, и каждой компании нужен административный пользователь.административный пользователь может управлять несколькими компаниями, поэтому в форме регистрации компании вы можете выбрать существующего пользователя из выпадающего списка.

модель представления компании выглядит примерно так:

public class CompanyViewModel {
    [Required]
    public string Name { get; set; }
    // other properties...
    public UserViewModel Administrator { get; set; }
    public IEnumerable<UserViewModel> AvailableUsers { get; set; }
}

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

public class UserViewModel {
    [Required]
    public string UserName { get; set; }
    [Required]
    public string Password { get; set; }
    // other properties...
}

в представлении регистрации компании:

<div><input type="radiobutton" name="chooseuser" id="existing"/>Choose an Existing User:</div.
<div>@Html.DropDownListFor(m => m.Administrator.Id, Model.AvailableUsers.Select(u => new SelectListItem { Text = string.Format("{0} - {1} {2}", u.UserName, u.FirstName, u.LastName), Value = u.Id.ToString() }), "<Choose existing user>", new { id = "existingusers" })
        </div>

<div><input type="radiobutton" name="chooseuser" id="createnew"/>Create a new User:</div>
<div><label>Username:</label> @Html.EditorFor(m => m.Administrator.UserName)</div>

С помощью javascript, основанного на выборе переключателей, раскрывающийся список отключается и отображается новая пользовательская форма, либо новая пользовательская форма скрывается, а раскрывающийся список включен.

Проблеманаходится в контроллере Сохранить действие после нажатия кнопки Сохранить, ModelState.IsValid имеет значение false, если выбран существующий пользователь и данные не заполнены в форме.Если пользователь решает ввести нового пользователя, проверка завершается успешно.

Каков наилучший способ справиться с этим?

Одним из вариантов является загрузка всех данных для всех пользователей в структуры данных в javascript, и когда значение изменяется в существующем выпадающем списке пользователей, могут быть заполнены скрытые поля формы «создать новый».Но это кажется неубедительным, поскольку пароли будут содержать HTML в виде обычного текста.я могу привыкнуть и использовать ajax для «создания новой» формы и заполнить идентификатор пользователя в исходной форме после сохранения нового пользователя, но я хотел бы сохранить все это в одной форме, если это возможно.

Кажется, мне бы хотелось, чтобы в идеале я мог бы загрузить существующие пользовательские данные из БД и заполнить состояние модели в действии контроллера Save, но написание этого кода вручную (даже с использованием отражения) кажется небрежным.Было бы неплохо, если бы для этого был встроенный метод.

Есть идеи?

Спасибо.

Ответы [ 2 ]

4 голосов
/ 13 марта 2011

Это типичный сценарий, который прекрасно иллюстрирует ограничения декларативной проверки (аннотации данных).Чтобы справиться с этим, вы можете написать собственный атрибут проверки, который будет применен к CompanyViewModel вместо отдельных свойств и позволит вам выполнять логику проверки в зависимости от того, какую кнопку-переключатель выберет пользователь (кстати, вам понадобится свойство длямодель вашего представления, которая будет представлять выбор переключателя).Проблема с валидаторами моделей заключается в том, что вам может быть трудно справиться с выделением ошибок.

Это одна из причин, по которой я использую FluentValidation.NET вместо аннотаций данных.Это позволяет мне отвести логику валидации от модели и выполнить ее в обязательном порядке.Это также позволяет мне иметь условные валидаторы, которые применяются на основе значений некоторых свойств модели представления (в этом случае это будет выбор переключателя).

0 голосов
/ 13 марта 2011

Возможно, вы захотите рассмотреть пользовательский Modelbinder.

Вот пример кода с моего сайта - это часть страницы оформления заказа для корзины покупок - пользователь может ввести адрес, но для США StateCdотправлено и для не США StateOrProvince отправлено.Поэтому мы смотрим на страну и удаляем любые ошибки модели для другого свойства, которое не применяется.

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

Важный код здесь bindingContext.ModelState.Remove(...), который удаляет состояние модели и позволяет IsValid возвращать true.

public class AddressModelBinder : DefaultModelBinder
{
    protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        base.OnModelUpdated(controllerContext, bindingContext);

        // get the address to validate
        var address = (Address)bindingContext.Model;

        // remove statecd for non-us
        if (address.IsUSA)
        {
            address.StateOrProvince = string.IsNullOrEmpty(address.StateCd) ? null : CountryCache.GetStateName(address.StateCd);
            bindingContext.ModelState.Remove(bindingContext.ModelName + ".StateOrProvince");
        }
        else
        {
            address.StateCd = null;
            bindingContext.ModelState.Remove(bindingContext.ModelName + ".StateCd");
        }

        // update country
        address.Country = CountryCache.GetCountry(address.CountryCode, true).Name;

        // validate US zipcode
        if (address.CountryCode == "US")
        {
            if (new Regex(@"^\d{5}([\-]\d{4})?$", RegexOptions.Compiled).Match(address.ZipOrPostal ?? "").Success == false)
            {
                bindingContext.ModelState.AddModelError(bindingContext.ModelName + ".ZipOrPostal", "The value " + address.ZipOrPostal + " is not a valid zipcode");
            }
        }

        // all other modelbinding attributes such as [Required] will be processed as normal
    }
}

Примечание: вам нужнозарегистрировать эту модель привязки в global.asax.Компонент привязки модели достаточно умен, чтобы позволить вам создавать разные привязки модели для любой части вашей модели, если он содержит разные объекты.

  ModelBinders.Binders[typeof(UI.Address)] = new AddressModelBinder();

Надеюсь, это поможет.Я думаю, что это относится к вашей ситуации.

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