Asp.NET MVC 2中的通用TimeSpan绑定

Car*_*any 9 .net timespan modelbinders asp.net-mvc-2

我有一个绑定到模型的输入表单.该模型具有TimeSpan属性,但只有当我输入时间为hh:mm或hh:mm:ss时,它才能正确获取值.我想要的是它捕获值,即使它被写为hhmm或hh.mm或hh.mm.ss或...我想要正确解析许多不同的格式.这可能吗?

谢谢!

Dre*_*kes 22

我为Carles的代码添加了一些增强功能,并想在这里分享它们,以防它们对其他人有用.

  • 确保如果没有模式成功解析时间,则仍然调用基数以显示验证错误(否则保留该值TimeSpan.Zero并且不会引发验证错误.)
  • 使用循环而不是链接if.
  • 支持使用AMPM足够.
  • 忽略空格.

这是代码:

public sealed class TimeSpanModelBinder : DefaultModelBinder
{
    private const DateTimeStyles _dateTimeStyles = DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.AssumeLocal | DateTimeStyles.NoCurrentDateDefault;

    protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor)
    {
        var form = controllerContext.HttpContext.Request.Form;

        if (propertyDescriptor.PropertyType.Equals(typeof(TimeSpan?)) || propertyDescriptor.PropertyType.Equals(typeof(TimeSpan)))
        {
            var text = form[propertyDescriptor.Name];
            TimeSpan time;
            if (text != null && TryParseTime(text, out time))
            {
                SetProperty(controllerContext, bindingContext, propertyDescriptor, time);
                return;
            }
        }

        // Either a different type, or we couldn't parse the string.
        base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
    }

    public static bool TryParseTime(string text, out TimeSpan time)
    {
        if (text == null)
            throw new ArgumentNullException("text");

        var formats = new[] {
            "HH:mm", "HH.mm", "HHmm", "HH,mm", "HH",
            "H:mm", "H.mm", "H,mm",
            "hh:mmtt", "hh.mmtt", "hhmmtt", "hh,mmtt", "hhtt",
            "h:mmtt", "h.mmtt", "hmmtt", "h,mmtt", "htt"
        };

        text = Regex.Replace(text, "([^0-9]|^)([0-9])([0-9]{2})([^0-9]|$)", "$1$2:$3$4");
        text = Regex.Replace(text, "^[0-9]$", "0$0");

        foreach (var format in formats)
        {
            DateTime value;
            if (DateTime.TryParseExact(text, format, CultureInfo.InvariantCulture, _dateTimeStyles, out value))
            {
                time = value.TimeOfDay;
                return true;
            }
        }
        time = TimeSpan.Zero;
        return false;
    }
}
Run Code Online (Sandbox Code Playgroud)

这可能看起来有点过头,但我希望我的用户能够输入几乎任何东西并让我的应用程序解决它.

它可以DateTime通过以下代码应用于所有实例Global.asax.cs:

ModelBinders.Binders.Add(typeof(TimeSpan), new TimeSpanModelBinder());
Run Code Online (Sandbox Code Playgroud)

或者仅针对特定的操作方法参数:

public ActionResult Save([ModelBinder(typeof(TimeSpanModelBinder))] MyModel model)
{ ... }
Run Code Online (Sandbox Code Playgroud)

这是一个简单的单元测试,只是为了验证一些潜在的输入/输出:

    [TestMethod]
    public void TimeSpanParsing()
    {
        var testData = new[] {
            new { Text = "100", Time = new TimeSpan(1, 0, 0) },
            new { Text = "10:00 PM", Time = new TimeSpan(22, 0, 0) },
            new { Text = "2", Time = new TimeSpan(2, 0, 0) },
            new { Text = "10", Time = new TimeSpan(10, 0, 0) },
            new { Text = "100PM", Time = new TimeSpan(13, 0, 0) },
            new { Text = "1000", Time = new TimeSpan(10, 0, 0) },
            new { Text = "10:00", Time = new TimeSpan(10, 0, 0) },
            new { Text = "10.00", Time = new TimeSpan(10, 0, 0) },
            new { Text = "13:00", Time = new TimeSpan(13, 0, 0) },
            new { Text = "13.00", Time = new TimeSpan(13, 0, 0) },
            new { Text = "10 PM", Time = new TimeSpan(22, 0, 0) },
            new { Text = "  10\t PM ", Time = new TimeSpan(22, 0, 0) },
            new { Text = "10PM", Time = new TimeSpan(22, 0, 0) },
            new { Text = "1PM", Time = new TimeSpan(13, 0, 0) },
            new { Text = "1 am", Time = new TimeSpan(1, 0, 0) },
            new { Text = "1 AM", Time = new TimeSpan(1, 0, 0) },
            new { Text = "1 pm", Time = new TimeSpan(13, 0, 0) },
            new { Text = "1 PM", Time = new TimeSpan(13, 0, 0) },
            new { Text = "01 PM", Time = new TimeSpan(13, 0, 0) },
            new { Text = "0100 PM", Time = new TimeSpan(13, 0, 0) },
            new { Text = "01.00 PM", Time = new TimeSpan(13, 0, 0) },
            new { Text = "01.00PM", Time = new TimeSpan(13, 0, 0) },
            new { Text = "1:00PM", Time = new TimeSpan(13, 0, 0) },
            new { Text = "1:00 PM", Time = new TimeSpan(13, 0, 0) },
            new { Text = "12,34", Time = new TimeSpan(12, 34, 0) },
            new { Text = "1012PM", Time = new TimeSpan(22, 12, 0) },
        };

        foreach (var test in testData)
        {
            try
            {
                TimeSpan time;
                Assert.IsTrue(TimeSpanModelBinder.TryParseTime(test.Text, out time), "Should parse {0}", test.Text);
                if (!Equals(time, test.Time))
                    Assert.Fail("Time parse failed.  Expected {0} but got {1}", test.Time, time);
            }
            catch (FormatException)
            {
                Assert.Fail("Received format exception with text {0}", test.Text);
            }
        }
    } 
Run Code Online (Sandbox Code Playgroud)

希望能帮助别人.


Laz*_*rus 4

是的 - 为您的模型对象编写自定义模型绑定器。SO 上有一个关于该主题的线程:ASP.NET MVC2 - Custom Model Binder Examples