User binding device for an object

I have the following controller action:

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

Where MyModelit looks like this:

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

So DefaultModelBinder should bind this without any problems. The only thing I want to use is a custom / custom binder for binding PropertyB, and I also want to reuse that binder. So I decided that this solution would include the ModelBinder attribute before the PropertyB, which of course does not work (the ModelBinder attribute is not allowed for properties). I see two solutions:

  • To use the action parameters for each individual property instead of the entire model (which I would not want, since the model has many properties):

    public ViewResult DoSomething(string propertyA, [ModelBinder(typeof(MyModelBinder))] propertyB)
    
  • , MyCustomType: List<int> ( )

  • MyModel, BindProperty, "PropertyB" . ?

?

+25
4

BindProperty, "PropertyB"

, " " , ,

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

BindProperty .

+15

, 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<PropertyBinderAttribute>()
              .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; }
    }
}
+14

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

, , , , , . MVC 5 , ASP.NET Core .

, , (, ): http://www.prideparrot.com/blog/archive/2012/6/customizing_property_binding_through_attributes. , , BindProperty StringArrayPropertyBindAttribute Vijaya Anand.

, , ( @jonathanconway), - , ; , IModelBinder.

The first approach is easier for me. There may be some flaws in the 1st approach that I did not know yet, although I am probably quite new to MVC at the moment.

In addition, I found that the ExtendedModelBinder class in the Vijaya Anand example is not needed in MVC 5. It seems that the DefaultModelBinder class that ships with MVC 5 is smart enough to interact with user model binding attributes.

+1
source

Source: https://habr.com/ru/post/1541720/


All Articles