2010-09-30 34 views
9

Tôi có một hình thức đầu vào được ràng buộc với một mô hình. Mô hình có thuộc tính TimeSpan, nhưng nó chỉ nhận giá trị đúng nếu tôi nhập thời gian là hh: mm hoặc hh: mm: ss. Những gì tôi muốn là cho nó để nắm bắt giá trị ngay cả khi nó được viết như hhmm hoặc hh.mm hoặc hh.mm.ss hoặc ... Tôi muốn nhiều định dạng khác nhau được phân tích cú pháp chính xác. Điều này có thể không?Generic TimeSpan ràng buộc trong Asp.NET MVC 2

Cảm ơn!

Trả lời

4

Có - viết một mô hình tùy chỉnh chất kết dính cho đối tượng mô hình của bạn. Có một chủ đề về những môn học mà ở đây trên SO: ASP.NET MVC2 - Custom Model Binder Examples

+0

Tuyệt vời! Đây là con đường để đi ... –

3

Đối với hồ sơ, dưới đây là cách tôi đã làm nó:

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); 
      } 
     } 
    } 
} 
+0

Cảm ơn bạn đã chia sẻ mã này. Bạn cũng có thể cho biết cách bạn chỉ định nó được sử dụng không? Bạn có thể cấu hình điều này chỉ cho một thuộc tính duy nhất trên một mô hình cụ thể, hay nó áp dụng chung cho tất cả các hoạt động liên kết thuộc tính 'TimeSpan'? –

+1

Tôi đã thêm điều này vào phương thức Application_Start của tệp Global_asax.cs: ModelBinders.Binders.DefaultBinder = new CustomTimeSpanModelBinder(); Tôi nghĩ bạn cũng có thể chỉ định điều này trên cơ sở hành động theo hành động bằng cách sử dụng chú thích. –

+1

Cảm ơn Carles. Tôi đã đăng thích ứng của tôi về mã của bạn như một câu trả lời quá. Trong trường hợp của tôi, tôi muốn xử lý nhiều chuỗi thời gian khác nhau trong ngày có thể được cung cấp và diễn giải một cách hợp lý. –

20

Tôi đã thêm một vài cải tiến mã Carles' và muốn chia sẻ chúng ở đây trong trường hợp chúng hữu ích cho người khác.

  • Đảm bảo rằng nếu không có mẫu phân tích thành công thời gian, sau đó vẫn gọi là cơ sở để hiển thị một lỗi xác nhận (nếu không thì giá trị còn lại như TimeSpan.Zero và không có lỗi xác nhận nâng lên.)
  • Sử dụng một vòng lặp thay bị xích if s.
  • Hỗ trợ việc sử dụng AMPM đủ.
  • Bỏ qua khoảng trắng.

Dưới đây là các mã:

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

Điều này có vẻ một chút trên đầu trang, nhưng tôi muốn người dùng của tôi để có thể nhập khá nhiều bất cứ điều gì và có ứng dụng của tôi làm việc nó ra.

Nó có thể được áp dụng cho tất cả các DateTime trường thông qua mã này trong Global.asax.cs:

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

Hoặc chỉ cần trên một số phương pháp hành động cụ thể:

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

và đây là một thử nghiệm đơn vị đơn giản chỉ để xác nhận một số đầu vào/đầu ra tiềm năng:

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

Hy vọng rằng sẽ giúp ai đó ra ngoài.

+0

Và với thử nghiệm quá? Rực rỡ. Cảm ơn! – Ted

+0

@Ted, bạn được chào đón. Đôi khi các thử nghiệm tạo ra tài liệu tốt nhất. –

+0

Đôi khi, bởi vì trong số nhiều thứ khác, chúng tôi thường xuyên cập nhật các bài kiểm tra của mình trong khi không đảm bảo tài liệu sẽ được thực hiện. Nếu các bài kiểm tra hoạt động, thì "tài liệu thực" là chính xác. – Ted

Các vấn đề liên quan