【问题标题】:Time-only editor template and validation仅限时间的编辑器模板和验证
【发布时间】:2017-08-24 05:20:33
【问题描述】:

在创建具有不显眼验证的仅时间输入字段时遇到一些问题,映射到不可为空的 DateTime 字段。

我正在使用数据库优先的方法。该数据库具有用于存储日期和时间的日期时间字段。我的意思是一些日期时间有真实的日期数据和 00:00:00 的时间,而其他的有无意义的实时日期数据。我知道这是由于 EF5 和 C# 数据类型的限制。现在正在使用 EF6,但不是特别打算更改数据库。

这是视图模型

[UIHint("Date")]
public DateTime MatchDate { get; set; }

[UIHint("Time")]
public DateTime StartTime { get; set; }

仅日期字段 MatchDate 正在工作。它使用 EditorFor 模板,Date.cshtml(如下)。它允许文本输入,正确附加一个 jQuery 日期选择器并正确验证服务器端和客户端,并且不显眼。

@model DateTime
@if (Model == DateTime.MinValue)
{
    @Html.TextBox("", "", new { @class = "datepicker" })
}
else
{   
    @Html.TextBox("", String.Format("{0:d/M/yyyy}", Model), new { @class = "datepicker" })
}

所以我为Time.cshtml创建了一个新的EditorFor模板

@model DateTime
@if (Model == DateTime.MinValue)    
{
    @Html.TextBox("", "", new { @class = "timepicker" })
}
else
{
    @Html.TextBox("", String.Format("{0:h\\:mm tt}", Model), new { @class = "timepicker" })
}

此时,Html 输入元素获得了一个不需要的data-val-date 属性,导致不显眼的日期 验证。我发现通过添加[DataType(DataType.Time)],不再添加data-val-date。虽然这是一件好事,但如果这正是(并且唯一)数据类型属性应该做的事情,我不是。所以这是问题的第一部分。

接下来,我为 12 小时时间格式创建了自定义验证规则(服务器端和客户端)。

注释:

public class TimeTwelveAttribute : ValidationAttribute, IClientValidatable
{
    private const string _ErrorMessage = "Invalid time format. Please use h:mm am/pm (TimeAttribute)";
    public TimeTwelveAttribute()
    {
        this.ErrorMessage = _ErrorMessage;
    }
    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        yield return new ModelClientValidationRule
        {
            ErrorMessage = _ErrorMessage,
            ValidationType = "timetwelve"
        };
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        DateTime time;
        if (value == null || !DateTime.TryParse(value.ToString(), out time))
        {
            return new ValidationResult(_ErrorMessage);
        }

        return ValidationResult.Success;
    }
}

javascript

$.validator.unobtrusive.adapters.addSingleVal("timetwelve");
$.validator.addMethod(
        "timetwelve",
        function (value, element) {
        return this.optional(element) || /^((0?[1-9]|1[012])(:[0-5]\d){1,2}(\ ?[AP]M))$/i.test(value);
        },
        "Invalid time format. Please use h:mm am/pm"
    );

但是,在 StartTime 中添加[TimeTwelve] 数据注解没有任何作用:

  • 正在进行服务器端验证,但它创建了一条我无法覆盖的不同错误消息:“值 'asdf' 对于开始时间无效。”
  • 如果我在时间字段中输入“asdf”,则不会弹出客户端验证消息。

我认为这是由于回发值被映射到 C# DateTimes 造成的。无论采用何种机制,它都具有更高的优先级并覆盖所有其他验证。作为练习,我将 StartTime 重写为一个字符串(并更新了模板等),果然[TimeTwelve] 在客户端和服务器端都能正常工作。我觉得这是 stackoverflow 上的任何相关问题都没有涉及到的方面。

所以我的问题的第二部分,鉴于我真的更喜欢在我的视图模型中使用 DateTimes,我怎样才能让验证工作?

This 是我能找到的最接近的问题,但来自 2013 年。

【问题讨论】:

  • 如果您只处理时间,那么您的视图模型中的属性应该是TimeSpan 而不是DateTime
  • DateTime 小时整数值限制为 23。要使用两个时间范围之间的差异,请改用 TimeSpan(它包括天、小时、分钟和秒)。
  • @StephenMuecke 谢谢。您将如何使用 TimeSpan 处理子午线指标?为什么 TimeSpan 比字符串更好?
  • 你的意思比DateTime 更好吗?但是再次阅读问题并注意到您不想更改数据库(尽管如果您只需要时间,它确实应该存储为TIME
  • 是的,如果您要提交“asdf”,DefaultModelBinder 无法将其转换为 DateTime,这会添加 值“asdf”对于开始时间无效。 ModelState 出错并且没有进行进一步的验证(因为没有意义)。

标签: c# asp.net-mvc datetime asp.net-mvc-5


【解决方案1】:

首先,为了让您的客户端验证正常工作,请将您的脚本更改为

$.validator.unobtrusive.adapters.add('timetwelve', function (options) {
    options.rules['timetwelve'] = {};
    options.messages['timetwelve'] = options.message;
});
// no need to hard code the message - it will use the one you have defined in the validation atribute
$.validator.addMethod('timetwelve', function (value, element) {
    return this.optional(element) || /^((0?[1-9]|1[012])(:[0-5]\d){1,2}(\ ?[AP]M))$/i.test(value);
});

或者你可以使用.addBool()方法

$.validator.unobtrusive.adapters.addBool("timetwelve");

至于接收值“asdf”对开始时间无效,如果您在文本框中输入“asdf”,这是默认行为。 DefaultModelBinder 使用 ValueConverters 将 Request 中的值解析为属性类型。由于asdf 无法转换为DateTime,因此添加了ModelState 错误并且不进行进一步验证(因为它已经无效并且没有意义)。当然,一旦客户端验证正常工作,您将永远不会到达那一点(除非用户禁用了 javascript 或其恶意用户)。

还要注意你的IsValid() 方法可以简单

protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
    return ValidationResult.Success;
}

当你到达这里时,DefaultModelBinder 已经设置了DateTime 的值(通过将今天的日期与表单中输入的时间相结合),因此无需再次尝试解析它。

【讨论】:

  • 对于以后阅读本文的任何人来说,只有几点:首先,我们并没有真正找到关于为什么我使用的验证脚本不起作用的答案。这是我们找到有效答案并继续前进的时代之一。其次,Steven 将我的注意力带到了 DefaultModelBinder,它是 MVC 框架的一部分,我没有真正考虑过它,因为它“通常只是工作”。
猜你喜欢
  • 2011-03-21
  • 1970-01-01
  • 2023-03-10
  • 2013-05-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-12
  • 2013-01-06
相关资源
最近更新 更多