Как DataAnnotationsModelBinder работает с пользовательским ViewModels?

Есть несколько вещей, о которых нужно знать.

  1. Во-первых, операторы console. * (Кроме .error) являются асинхронными, вы передаете объект документа по ссылке, который может фактически измениться между передачей его в console.FN и фактическим выводом в окно журнала.
  2. Во-вторых, браузер имеет доступ к большему, чем вы могли бы через JS API напрямую с точки зрения опроса.
  3. В-третьих, сам документ может иметь цепочку наследования, которая включает в себя такие вещи, как домен, который не является частью самого экземпляра документа.
  4. В-четвертых, не все свойства являются итеративными по своей структуре, но все же могут отображаться в журнале (см. № 2 выше)
5
задан Peter Mortensen 12 February 2011 в 07:40
поделиться

2 ответа

Сегодня я столкнулся с той же проблемой. Как и вы, я не привязываю свое представление напрямую к моей модели, но использую промежуточный класс ViewDataModel, который содержит экземпляр модели и любые параметры / конфигурации, которые я хотел бы отправить в представление.

В итоге я изменил BindProperty в DataAnnotationsModelBinder, чтобы обойти NullReferenceException , и мне лично не нравилось связывать свойства только в том случае, если они действительны (см. Причины ниже).

protected override void BindProperty(ControllerContext controllerContext,
                                         ModelBindingContext bindingContext,
                                         PropertyDescriptor propertyDescriptor) {
    string fullPropertyKey = CreateSubPropertyName(bindingContext.ModelName, propertyDescriptor.Name);

    // Only bind properties that are part of the request
    if (bindingContext.ValueProvider.DoesAnyKeyHavePrefix(fullPropertyKey)) {
        var innerContext = new ModelBindingContext() {
            Model = propertyDescriptor.GetValue(bindingContext.Model),
            ModelName = fullPropertyKey,
            ModelState = bindingContext.ModelState,
            ModelType = propertyDescriptor.PropertyType,
            ValueProvider = bindingContext.ValueProvider
        };

        IModelBinder binder = Binders.GetBinder(propertyDescriptor.PropertyType);
        object newPropertyValue = ConvertValue(propertyDescriptor, binder.BindModel(controllerContext, innerContext));
        ModelState modelState = bindingContext.ModelState[fullPropertyKey];
        if (modelState == null)
        {
            var keys = bindingContext.ValueProvider.FindKeysWithPrefix(fullPropertyKey);
            if (keys != null && keys.Count() > 0)
                modelState = bindingContext.ModelState[keys.First().Key];
        }
        // Only validate and bind if the property itself has no errors
        //if (modelState.Errors.Count == 0) {
            SetProperty(controllerContext, bindingContext, propertyDescriptor, newPropertyValue);
            if (OnPropertyValidating(controllerContext, bindingContext, propertyDescriptor, newPropertyValue)) {

                OnPropertyValidated(controllerContext, bindingContext, propertyDescriptor, newPropertyValue);
            }
        //}

        // There was an error getting the value from the binder, which was probably a format
        // exception (meaning, the data wasn't appropriate for the field)
        if (modelState.Errors.Count != 0) {
            foreach (var error in modelState.Errors.Where(err => err.ErrorMessage == "" && err.Exception != null).ToList()) {
                for (var exception = error.Exception; exception != null; exception = exception.InnerException) {
                    if (exception is FormatException) {
                        string displayName = GetDisplayName(propertyDescriptor);
                        string errorMessage = InvalidValueFormatter(propertyDescriptor, modelState.Value.AttemptedValue, displayName);
                        modelState.Errors.Remove(error);
                        modelState.Errors.Add(errorMessage);
                        break;
                    }
                }
            }
        }
    }
}

Я также изменил это так, чтобы it всегда связывает данные в свойстве независимо от того, действительны они или нет. Он не был должным образом протестирован, хотя он прошел модульные тесты в проекте , который начал Брайан Уилсон, и большую часть моего собственного ограниченного тестирования. Чтобы полностью завершить этот вопрос, я хотел бы услышать Брэда Уилсона мысли об этом решении.

8
ответ дан 13 December 2019 в 19:35
поделиться

Как заметил Мартин, исправить эту проблему просто.

В методе BindProperty вы найдете эту строку кода:

if (modelState.Errors.Count == 0) {

Ее следует изменить на:

if (modelState == null || modelState.Errors.Count == 0) {

] Мы планируем включить поддержку DataAnnotations в MVC 2, который будет включать DataAnnotationsModelBinder. Эта функция будет частью первой CTP.

3
ответ дан 13 December 2019 в 19:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: