Пользовательское связующее устройство для объекта

У меня есть следующее действие controllerа:

[HttpPost] public ViewResult DoSomething(MyModel model) { // do something return View(); } 

Где MyModel выглядит так:

 public class MyModel { public string PropertyA {get; set;} public IList PropertyB {get; set;} } 

Поэтому DefaultModelBinder должен связывать это без проблем. Единственное, что я хочу использовать специальное / настраиваемое связующее для привязки PropertyB и я также хочу повторно использовать это связующее. Поэтому я решил, что это решение будет включать атрибут ModelBinder перед PropertyB, который, конечно, не работает (атрибут ModelBinder не допускается для свойств). Я вижу два решения:

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

     public ViewResult DoSomething(string propertyA, [ModelBinder(typeof(MyModelBinder))] propertyB) 
  2. Чтобы создать новый тип, скажем, MyCustomType: List и зарегистрируйте MyCustomType: List модели для этого типа (это опция)

  3. Возможно, для создания связующего для MyModel, переопределите BindProperty и если свойство "PropertyB" связывает свойство с моим настраиваемым связующим. Это возможно?

Есть ли другое решение?

переопределить BindProperty, и если свойство «PropertyB» связывает свойство с моим настраиваемым связующим

Это хорошее решение, но вместо того, чтобы проверять «PropertyB», лучше проверить свои собственные атрибуты, которые определяют связывание на уровне свойств, например

 [PropertyBinder(typeof(PropertyBBinder))] public IList PropertyB {get; set;} 

Здесь вы можете увидеть пример BindProperty.

Мне действительно нравится ваше третье решение, только я бы сделал это универсальным решением для всех моделей ModelBinders, поместив его в настраиваемое связующее, которое наследуется от DefaultModelBinder и настроено как связующее устройство модели по умолчанию для вашего приложения MVC.

Затем вы должны сделать этот новый DefaultModelBinder автоматически привязанным к любому свойству, украшенному атрибутом PropertyBinder , используя тип, указанный в параметре.

Я получил эту идею из этой замечательной статьи: http://aboutcode.net/2011/03/12/mvc-property-binder.html .

Я также покажу вам свое решение:

Мой DefaultModelBinder :

 namespace MyApp.Web.Mvc { public class DefaultModelBinder : System.Web.Mvc.DefaultModelBinder { protected override void BindProperty( ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor) { var propertyBinderAttribute = TryFindPropertyBinderAttribute(propertyDescriptor); if (propertyBinderAttribute != null) { var binder = CreateBinder(propertyBinderAttribute); var value = binder.BindModel(controllerContext, bindingContext, propertyDescriptor); propertyDescriptor.SetValue(bindingContext.Model, value); } else // revert to the default behavior. { base.BindProperty(controllerContext, bindingContext, propertyDescriptor); } } IPropertyBinder CreateBinder(PropertyBinderAttribute propertyBinderAttribute) { return (IPropertyBinder)DependencyResolver.Current.GetService(propertyBinderAttribute.BinderType); } PropertyBinderAttribute TryFindPropertyBinderAttribute(PropertyDescriptor propertyDescriptor) { return propertyDescriptor.Attributes .OfType() .FirstOrDefault(); } } } 

Интерфейс IPropertyBinder :

 namespace MyApp.Web.Mvc { interface IPropertyBinder { object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext, MemberDescriptor memberDescriptor); } } 

PropertyBinderAttribute :

 namespace MyApp.Web.Mvc { public class PropertyBinderAttribute : Attribute { public PropertyBinderAttribute(Type binderType) { BinderType = binderType; } public Type BinderType { get; private set; } } } 

Пример связующего свойства:

 namespace MyApp.Web.Mvc.PropertyBinders { public class TimeSpanBinder : IPropertyBinder { readonly HttpContextBase _httpContext; public TimeSpanBinder(HttpContextBase httpContext) { _httpContext = httpContext; } public object BindModel( ControllerContext controllerContext, ModelBindingContext bindingContext, MemberDescriptor memberDescriptor) { var timeString = _httpContext.Request.Form[memberDescriptor.Name].ToLower(); var timeParts = timeString.Replace("am", "").Replace("pm", "").Trim().Split(':'); return new TimeSpan( int.Parse(timeParts[0]) + (timeString.Contains("pm") ? 12 : 0), int.Parse(timeParts[1]), 0); } } } 

Пример используемого выше связующего свойства:

 namespace MyApp.Web.Models { public class MyModel { [PropertyBinder(typeof(TimeSpanBinder))] public TimeSpan InspectionDate { get; set; } } } 

@ Ответ jonathanconway велик, но я хотел бы добавить незначительные детали.

Вероятно, лучше переопределить метод GetPropertyValue вместо BindProperty , чтобы дать механизм проверки DefaultBinder .

 protected override object GetPropertyValue( ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder) { PropertyBinderAttribute propertyBinderAttribute = TryFindPropertyBinderAttribute(propertyDescriptor); if (propertyBinderAttribute != null) { propertyBinder = CreateBinder(propertyBinderAttribute); } return base.GetPropertyValue( controllerContext, bindingContext, propertyDescriptor, propertyBinder); } 

Прошло шесть лет с тех пор, как был задан этот вопрос, я предпочел бы использовать это пространство для обобщения обновления, а не для создания совершенно нового решения. На момент написания статьи MVC 5 уже давно существует, и ASP.NET Core только что вышел.

Я следовал подходу, рассмотренному в сообщении, написанном Виджаей Анандом (кстати, благодаря Виджае): http://www.prideparrot.com/blog/archive/2012/6/customizing_property_binding_through_attributes . И стоит отметить, что логика привязки данных помещается в пользовательский class атрибутов, который является методом BindProperty classа StringArrayPropertyBindAttribute в примере Vijaya Anand.

Тем не менее, во всех других статьях по этой теме, которые я прочитал (включая решение @ jonathanconway), пользовательский class атрибутов – это всего лишь шаг, который заставляет фреймворк найти правильное настраиваемое связующее устройство для применения; и логика привязки помещается в это настраиваемое связующее устройство, которое обычно является объектом IModelBinder.

Первый подход для меня проще. Могут быть некоторые недостатки 1-го подхода, которые я еще не знал, хотя, пожалуй, я довольно новичок в области MVC на данный момент.

Кроме того, я обнаружил, что class ExtendedModelBinder в примере Vijaya Anand не нужен в MVC 5. Кажется, что class DefaultModelBinder, который поставляется с MVC 5, достаточно умен, чтобы взаимодействовать с атрибутами привязки пользовательской модели.

  • Программно прокрутите до метки привязки
  • Связывание со статическим свойством
  • WPF Binding - значение по умолчанию для пустой строки
  • как связать datatable с datagridview в c #
  • Разница между {Binding PropertyName} и {Binding Path = PropertyName}
  • Как форматировать столбцы DateTime в DataGridView?
  • WinForms DataGridView - привязка к объекту со свойством списка (переменное число столбцов)
  • WPF StringFormat на содержании ярлыков
  • Привязка списка в @RequestParam
  • Привязка видимости кнопки к значению bool в ViewModel
  • Как скрыть столбцы datagrid wpf в зависимости от свойства
  • Давайте будем гением компьютера.