Пользовательское связующее устройство DateTime в Asp.net MVC

Я хотел бы написать свое собственное связующее устройство для типа DateTime . Прежде всего, я хотел бы написать новый атрибут, который я могу прикрепить к свойству модели, например:

 [DateTimeFormat("dMyyyy")] public DateTime Birth { get; set,} 

Это легкая часть. Но связующая часть немного сложнее. Я хотел бы добавить новое связующее устройство для типа DateTime . Я могу либо

  • реализовать интерфейс IModelBinder и написать собственный BindModel()
  • унаследовать от DefaultModelBinder и переопределить BindModel()

Моя модель имеет свойство, как видно выше ( Birth ). Поэтому, когда модель пытается связать данные запроса с этим свойством, BindModel(controllerContext, bindingContext) моего BindModel(controllerContext, bindingContext) . Все нормально, но. Как получить атрибуты свойства из controllerа / bindingContext, правильно ли вывести дату ? Как я могу получить PropertyDesciptor для свойства Birth ?

редактировать

Из-за разделения проблем мой модельный class определен в сборке, которая не (и не должна) ссылается на сборку System.Web.MVC. Настройка атрибута привязки (аналогично примеру Скотта Ханзельмана ) не является здесь.

Я не думаю, что вы должны ставить специфические для локали атрибуты на модели.

Два других возможных решения этой проблемы:

  • Пусть ваши страницы транслитерируют даты из формата, соответствующего локали, в общий формат, такой как yyyy-mm-dd в JavaScript. (Работает, но требует JavaScript.)
  • Напишите привязку модели, которая учитывает текущую культуру пользовательского интерфейса при parsingе дат.

Чтобы ответить на ваш реальный вопрос, способ получить пользовательские атрибуты (для MVC 2) – написать AssociatedMetadataProvider .

вы можете изменить стандартное связующее устройство, чтобы использовать культуру пользователя, используя IModelBinder

 public class DateTimeBinder : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); bindingContext.ModelState.SetModelValue(bindingContext.ModelName, value); return value.ConvertTo(typeof(DateTime), CultureInfo.CurrentCulture); } } public class NullableDateTimeBinder : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); bindingContext.ModelState.SetModelValue(bindingContext.ModelName, value); return value == null ? null : value.ConvertTo(typeof(DateTime), CultureInfo.CurrentCulture); } } 

И в Global.Asax добавьте следующее в Application_Start ():

 ModelBinders.Binders.Add(typeof(DateTime), new DateTimeBinder()); ModelBinders.Binders.Add(typeof(DateTime?), new NullableDateTimeBinder()); 

Читайте больше в этом отличном блоге, в котором описывается, почему команда Mvc Framework реализовала культуру по умолчанию для всех пользователей.

У меня была эта очень большая проблема, и после нескольких часов попыток и неудач я получил рабочее решение, как вы просили.

Прежде всего, поскольку наличие связующего только на свойстве невозможно, вам нужно реализовать полный ModelBinder. Поскольку вы не хотите связывать все одно свойство, но только тот, который вам небезразличен, вы можете наследовать от DefaultModelBinder, а затем привязать одно свойство:

 public class DateFiexedCultureModelBinder : DefaultModelBinder { protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor) { if (propertyDescriptor.PropertyType == typeof(DateTime?)) { try { var model = bindingContext.Model; PropertyInfo property = model.GetType().GetProperty(propertyDescriptor.Name); var value = bindingContext.ValueProvider.GetValue(propertyDescriptor.Name); if (value != null) { System.Globalization.CultureInfo cultureinfo = new System.Globalization.CultureInfo("it-CH"); var date = DateTime.Parse(value.AttemptedValue, cultureinfo); property.SetValue(model, date, null); } } catch { //If something wrong, validation should take care } } else { base.BindProperty(controllerContext, bindingContext, propertyDescriptor); } } } 

В моем примере я разбираю дату с культурой, но вы можете сделать то, что вы хотите сделать. Вы должны создать атрибут CustomAttribute (например, DateTimeFormatAttribute) и поместить его поверх своего свойства:

 [DateTimeFormat("dMyyyy")] public DateTime Birth { get; set,} 

Теперь в методе BindProperty вместо поиска свойства DateTime вы можете искать свойство с вами DateTimeFormatAttribute, захватить формат, указанный вами в конструкторе, а затем проанализировать дату с помощью DateTime.ParseExact

Надеюсь, это поможет, мне потребовалось очень много времени с этим решением. На самом деле было легко получить это решение, как только я знал, как его искать 🙁

Вы можете реализовать пользовательское связывание DateTime так, но вы должны заботиться о предполагаемой культуре и ценности из фактического запроса клиента. Можете ли вы получить дату, например, mm / dd / yyyy в en-US, и хотите, чтобы она конвертировалась в культуру системы en-GB (которая была бы как dd / mm / yyyy) или инвариантная культура, как и мы, тогда вы необходимо проанализировать его до и использовать статический фасад Convert, чтобы изменить его в своем поведении.

  public class DateTimeModelBinder : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { var valueResult = bindingContext.ValueProvider .GetValue(bindingContext.ModelName); var modelState = new ModelState {Value = valueResult}; var resDateTime = new DateTime(); if (valueResult == null) return null; if ((bindingContext.ModelType == typeof(DateTime)|| bindingContext.ModelType == typeof(DateTime?))) { if (bindingContext.ModelName != "Version") { try { resDateTime = Convert.ToDateTime( DateTime.Parse(valueResult.AttemptedValue, valueResult.Culture, DateTimeStyles.AdjustToUniversal).ToUniversalTime(), CultureInfo.InvariantCulture); } catch (Exception e) { modelState.Errors.Add(EnterpriseLibraryHelper.HandleDataLayerException(e)); } } else { resDateTime = Convert.ToDateTime( DateTime.Parse(valueResult.AttemptedValue, valueResult.Culture), CultureInfo.InvariantCulture); } } bindingContext.ModelState.Add(bindingContext.ModelName, modelState); return resDateTime; } } 

В любом случае, зависимость от культуры в формате DateTime в приложении без гражданства может быть жестоко … Особенно, когда вы работаете с JSON на javascript clientide и backwards.

  • WPF StringFormat на содержании ярлыков
  • WPF: привязать DataGrid к списку
  • Свяжите элемент с двумя источниками
  • Как связать WPF DataGrid с переменным числом столбцов?
  • WPF DataGrid: свойство CanContentScroll, вызывающее нечетное поведение
  • Как повысить свойства Измененные события в зависимости от свойства?
  • Почему обновление привязки не реализует INotifyPropertyChanged?
  • ComboBox.SelectedValue не обновляется из источника привязки
  • Привязка списка в @RequestParam
  • UITableView - лучшее редактирование посредством привязки?
  • Список привязок к DataGridView в WinForm
  • Давайте будем гением компьютера.