2010-09-30 1 views
9

У меня есть форма ввода, привязанная к модели. Модель имеет свойство TimeSpan, но оно правильно вводит значение, если я вводим время как hh: mm или hh: mm: ss. Я хочу, чтобы он фиксировал значение, даже если оно написано как hhmm или hh.mm или hh.mm.ss или ... Я хочу, чтобы все различные форматы были правильно проанализированы. Это возможно?Общее связывание TimeSpan в Asp.NET MVC 2

Спасибо!

ответ

4

Да - напишите собственное связующее устройство модели для объекта модели. Там есть нить только об этой теме здесь на SO: ASP.NET MVC2 - Custom Model Binder Examples

+0

Отлично! Это путь ... –

3

Для записи, вот как я это сделал:

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

Спасибо, что поделились этим кодом. Не могли бы вы также показать, как вы указали его для использования? Можете ли вы сконфигурировать это только для одного свойства на определенной модели или применительно к глобальным всем операциям связывания свойств TimeSpan? –

+1

Я добавил это к методу Application_Start файла Global_asax.cs: ModelBinders.Binders.DefaultBinder = new CustomTimeSpanModelBinder(); Я думаю, вы также можете указать это на основе действия по действиям, используя аннотации. –

+1

Спасибо Карлс. Я также внесли свою адаптацию вашего кода в качестве ответа. В моем случае я хотел обрабатывать столько разных строк времени, сколько можно было бы разумно предоставить и интерпретировать. –

20

Я добавил несколько усовершенствований кода Карлес и хотел бы поделиться им здесь, в случае они полезны для других.

  • Убедитесь, что, если нет модели успешно не разобрать время, то до сих пор называют базу для того, чтобы показать ошибку проверки (в противном случае значение остается в качестве 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); 
      } 
     } 
    } 

Надеюсь, что кто-то поможет.

+0

И с испытаниями тоже? Brilliant. Благодаря! – Ted

+0

@ Тебе, пожалуйста. Иногда тесты делают лучшую документацию. –

+0

Более чем иногда, потому что среди многих других вещей мы обычно постоянно обновляем наши тесты, пока нет гарантии, что документация будет. Если тесты работают, то «настоящая документация» верна. – Ted

 Смежные вопросы

  • Нет связанных вопросов^_^