Управляйте значением модели, прежде чем передать его в DefaultModelBinder.BindModel - PullRequest
5 голосов
/ 04 января 2012

Некоторые свойства decimal и decimal? в моей модели представления помечены как тип данных «Процент» вместе с другими аннотациями данных, например:

[DataType("Percent")]
[Display(Name = "Percent of foo completed")]
[Range(0, 1)]
public decimal? FooPercent { get; set; }

Я бы хотел предоставить пользователю некоторую гибкость при вводе данных, т. Е. С или без знака процента, промежуточных пробелов и т. Д. Но я все же хочу использовать поведение DefaultModelBinder, чтобы получить всю его функциональность. например, проверка RangeAttribute и добавление соответствующих проверочных сообщений.

Есть ли способ проанализировать и изменить значение модели, а затем передать его? Вот что я пытаюсь, но получаю исключение во время выполнения. (Не обращайте внимания на реальную логику синтаксического анализа; это не окончательная форма. На данный момент меня просто интересует вопрос замены модели.)

public class PercentModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext,
                                     ModelBindingContext bindingContext)
    {
        if (bindingContext.ModelMetadata.DataTypeName == "Percent")
        {
            ValueProviderResult result =
                bindingContext.ValueProvider.GetValue(
                    bindingContext.ModelName);
            if (result != null)
            {
                string stringValue =
                    (string)result.ConvertTo(typeof(string));
                decimal decimalValue;
                if (!string.IsNullOrWhiteSpace(stringValue) &&
                    decimal.TryParse(
                        stringValue.TrimEnd(new char[] { '%', ' ' }),
                        out decimalValue))
                {
                    decimalValue /= 100.0m;

                    // EXCEPTION : This property setter is obsolete, 
                    // because its value is derived from 
                    // ModelMetadata.Model now.
                    bindingContext.Model = decimalValue;
                }
            }
        }

        return base.BindModel(controllerContext, bindingContext);
    }
}

1 Ответ

5 голосов
/ 04 января 2012

Неважно, это было фундаментальное недопонимание того, где проверка происходит в цикле MVC.Потратив некоторое время в исходном коде MVC, я вижу, как это работает.

В случае, если это полезно для других, вот что работает для меня:

[DataType("Percent")]
[Display(Name = "Percent of foo completed")]
[Range(0.0d, 1.0d, ErrorMessage="The field {0} must be between {1:P0} and {2:P0}.")]
public decimal? FooPercent { get; set; }

И вbinder, вы просто возвращаете значение:

public class PercentModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext,
                                     ModelBindingContext bindingContext)
    {
        if (bindingContext.ModelMetadata.DataTypeName == "Percent")
        {
            ValueProviderResult result =
                bindingContext.ValueProvider.GetValue(
                    bindingContext.ModelName);
            if (result != null)
            {
                string stringValue =
                    (string)result.ConvertTo(typeof(string));
                decimal decimalValue;
                if (!string.IsNullOrWhiteSpace(stringValue) &&
                    decimal.TryParse(
                        stringValue.TrimEnd(new char[] { '%', ' ' }),
                        out decimalValue))
                {
                    return decimalValue / 100.0m;
                }
            }
        }

        return base.BindModel(controllerContext, bindingContext);
    }
}
...