代码之家  ›  专栏  ›  技术社区  ›  Carles Company

中的通用TimeSpan绑定Asp.NETMVC 2型

  •  9
  • Carles Company  · 技术社区  · 14 年前

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

    谢谢!

    3 回复  |  直到 12 年前
        2
  •  22
  •   Drew Noakes    12 年前

    我在Carles的代码中添加了一些增强功能,希望在这里与大家分享,以防对其他人有用。

    • 确保如果没有模式成功解析时间,那么仍然调用base以显示验证错误(否则该值保留为空) TimeSpan.Zero
    • 使用循环而不是链接 if s。
    • 支持使用 AM PM 够了。
    • 忽略空白。

    代码如下:

    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;
        }
    }
    

    这似乎有点过头了,但我希望我的用户能够输入几乎任何东西,让我的应用程序解决它。

    它可以适用于所有 DateTime 在中通过此代码创建实例 Global.asax.cs :

    ModelBinders.Binders.Add(typeof(TimeSpan), new TimeSpanModelBinder());
    

    public ActionResult Save([ModelBinder(typeof(TimeSpanModelBinder))] MyModel model)
    { ... }
    

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

        [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);
                }
            }
        } 
    

    希望能帮上忙。

        3
  •  3
  •   Carles Company    14 年前

    作为记录,我是这样做的:

    using System;
    using System.Globalization;
    using System.Web.Mvc;
    
    namespace Utils.ModelBinders
    {
        public class CustomTimeSpanModelBinder : DefaultModelBinder
        {
            protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor)
            {
                var form = controllerContext.HttpContext.Request.Form;
    
                if (propertyDescriptor.PropertyType.Equals(typeof(TimeSpan?)))
                {
                    var text = form[propertyDescriptor.Name];
                    DateTime value;
                    if (DateTime.TryParseExact(text, "HH:mm", CultureInfo.InvariantCulture, DateTimeStyles.None, out value))
                            SetProperty(controllerContext,bindingContext,propertyDescriptor,value.TimeOfDay);
                    else if (DateTime.TryParseExact(text, "HH.mm", CultureInfo.InvariantCulture, DateTimeStyles.None, out value))
                        SetProperty(controllerContext, bindingContext, propertyDescriptor, value.TimeOfDay);
                    else if (DateTime.TryParseExact(text, "HHmm", CultureInfo.InvariantCulture, DateTimeStyles.None, out value))
                        SetProperty(controllerContext, bindingContext, propertyDescriptor, value.TimeOfDay);
                    else if (DateTime.TryParseExact(text, "HH,mm", CultureInfo.InvariantCulture, DateTimeStyles.None, out value))
                        SetProperty(controllerContext, bindingContext, propertyDescriptor, value.TimeOfDay);
                    else if (DateTime.TryParseExact(text, "HH", CultureInfo.InvariantCulture, DateTimeStyles.None, out value))
                        SetProperty(controllerContext, bindingContext, propertyDescriptor, value.TimeOfDay);
                }
                else
                {
                    base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
                }
            }
        }
    }