0

在 ASP.NET MVC 4 应用程序中,我有一个包含可为空TimeSpan属性的视图模型:

[DisplayName("My time")]
public TimeSpan? MyTime { get; set; }

它绑定到视图中的输入元素:

@Html.EditorFor(model => model.MyTime)

输入框在自定义编辑器模板的帮助下呈现TimeSpan.cshtml

@model Nullable<System.TimeSpan>

@Html.TextBox("", (Model.HasValue
    ? Model.Value.ToString(@"hh\:mm") : string.Empty),
    new { @class = "text-box single-line hasTimepicker" data_timepicker = true })

现在,如果我输入以下两种无效时间并提交页面,我会得到模型绑定器的以下不同行为:

  • 如果我输入一个字母,"a"请在输入元素中说ModelError,当我钻入ModelState.Values集合时,该属性的ErrorMessage属性设置为消息 ( "The value \"a\" for \"My time\" is invalid."),Exception属性为null. MyTime的界值为null

    ErrorMessage显示在页面的验证摘要中。

  • "25:12"如果我在输入元素中输入了一个无效时间,比如说,ModelError这个属性的ErrorMessage属性设置为一个空字符串,但该Exception属性设置为一个类型的异常,一个类型InvalidOperationException的内部异常OverflowException告诉我TimeSpan无法分析,因为其中一个其数值成分超出有效范围。MyTime的界值为null

    同样,ErrorMessage显示在页面的验证摘要中。但是因为它是空的,所以它不是很有用。

理想情况下,对于无效输入的第二种情况,我希望有与第一种情况相同的错误消息,例如"The value \"25:12\" for \"My time\" is invalid.".

我怎么解决这个问题?

编辑

自定义验证属性显然在这里没有帮助,因为当模型绑定器已经检测到无效值时,不会为上述示例中的无效输入调用它。我曾尝试过这种方法但没有成功。

4

2 回答 2

2

问题是错误发生在模型绑定中,这就是您需要捕获和检查它的地方。

我有一个 Timespan 模型活页夹和编辑器模板,TimeSpan?它应该可以满足您在 Gist 上的需要

于 2013-05-10T10:19:58.987 回答
1

@Chao 的回答让我走上了使用自定义模型绑定器的正确轨道。

因为我希望尽可能多地保持默认模型绑定器的功能(输入格式的灵活性、本地化等)不变,并且只有在用户输入"25:12"或类似的情况下才会为用户提供有用的错误消息,所以我创建了以下绑定器仅检测默认模型绑定器是否已将OverflowException(作为内部异常)添加到模型状态,如果是,我将错误消息添加到状态:

public class TimeSpanModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext,
        ModelBindingContext bindingContext)
    {
        object timeSpanValue = base.BindModel(controllerContext, bindingContext);

        var modelState = bindingContext.ModelState[bindingContext.ModelName];
        var hasOverflowException = modelState.Errors
            .Any(e => e.Exception != null &&
                e.Exception.InnerException is OverflowException);

        if (hasOverflowException)
        {
            var rawValues = modelState.Value.RawValue as string[];
            if (rawValues != null && rawValues.Length >= 1)
            {
                bindingContext.ModelState.AddModelError(
                    bindingContext.ModelName, string.Format(
                        "The value \"{0}\" for field \"{1}\" is invalid.",
                        rawValues[0],
                        bindingContext.ModelMetadata.GetDisplayName()));
            }
        }

        return timeSpanValue;
    }
}

添加到global.asax/Application_Start()ModelBinders集合中:

ModelBinders.Binders.Add(typeof(TimeSpan), new TimeSpanModelBinder());
ModelBinders.Binders.Add(typeof(TimeSpan?), new TimeSpanModelBinder());
于 2013-05-10T18:03:48.260 回答