Question

Je l'action du contrôleur suivant:

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

MyModel ressemble à ceci:

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

DefaultModelBinder devrait lier cela sans problème. La seule chose est que je veux utiliser un liant spécial / custom pour la liaison PropertyB et je veux aussi réutiliser ce liant. Donc, je pensais que la solution serait de mettre un attribut ModelBinder avant la PropertyB qui bien sûr ne fonctionne pas (attribut ModelBinder est pas autorisée sur une des propriétés). Je vois deux solutions:

  1. Pour utiliser les paramètres d'action sur chaque propriété au lieu du modèle entier (que je ne préférerais pas que le modèle a beaucoup de propriétés) comme ceci:

    public ViewResult DoSomething(string propertyA, [ModelBinder(typeof(MyModelBinder))] propertyB)
    
  2. Pour créer un nouveau type permet de dire MyCustomType: List<int> et registre liant modèle pour ce type (ce qui est une option)

  3. Peut-être pour créer un liant pour MyModel, passer outre BindProperty et si la propriété est "PropertyB" lier la propriété avec mon classeur personnalisé. Est-ce possible?

Y at-il une autre solution?

Était-ce utile?

La solution

  

override bindProperty et si le   la propriété est « PropertyB » lier la   propriété avec mon classeur personnalisé

C'est une bonne solution, mais au lieu de vérifier « est PropertyB » vous feriez mieux de vérifier vos propres attributs personnalisés qui définissent des liants niveau de la propriété, comme

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

Vous pouvez voir un exemple de bindProperty override ici .

Autres conseils

En fait, je comme votre troisième solution, que je ferais une solution générique pour tous les ModelBinders, en le mettant dans un classeur personnalisé qui hérite de DefaultModelBinder et est configuré pour être le modèle de liaison par défaut pour votre application MVC.

Ensuite, vous faire de cette nouvelle DefaultModelBinder lier automatiquement une propriété qui est décorée avec un attribut PropertyBinder, en utilisant le type fourni dans le paramètre.

J'ai eu l'idée de cet excellent article: http://aboutcode.net/2011/ 03/12 / mvc-propriété-binder.html .

Je vais aussi vous montrer mon avis sur la solution:

Mon 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();
        }
    }
}

Mon interface IPropertyBinder:

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

Mon PropertyBinderAttribute:

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

        public Type BinderType { get; private set; }
    }
}

Un exemple d'un liant de bien:

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

Exemple de liant de la propriété ci-dessus étant utilisés:

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

@ jonathanconway est grande, mais je voudrais ajouter un détail mineur.

Il est sans doute préférable de remplacer la méthode GetPropertyValue au lieu de BindProperty afin de donner le mécanisme de validation de la DefaultBinder une chance de travailler.

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

Il a été 6 ans que cette question a été posée, je préfère prendre cet espace pour résumer la mise à jour, au lieu de fournir une toute nouvelle solution. Au moment de l'écriture, MVC 5 a été autour depuis un certain temps, et ASP.NET de base vient de sortir.

J'ai suivi l'approche examinée dans le post écrit par Anand Vijaya (BTW, grâce à Vijaya): http://www.prideparrot.com/blog/archive/2012/6/customizing_property_binding_through_attributes . Et une chose à noter est que, les données logiques de liaison est placé dans la classe attribut personnalisé, qui est la méthode bindProperty de la classe StringArrayPropertyBindAttribute dans l'exemple de Vijaya Anand.

Cependant, dans tous les autres articles sur ce sujet que j'ai lu (y compris la solution de @ jonathanconway), attribut personnalisé classe est seulement une pierre étape qui mène le cadre à trouver le liant modèle correct personnalisé à appliquer; et la logique de liaison est placé dans le classeur de modèle personnalisé, qui est généralement un objet IModelBinder.

La 1ère approche est plus simple pour moi. Il peut y avoir des lacunes de la 1ère approche, que je ne l'ai pas encore connu, mais, coz je suis assez nouveau pour framework MVC pour le moment.

De plus, je trouve que la classe ExtendedModelBinder dans l'exemple de Vijaya Anand est inutile dans MVC 5. Il semble que la classe DefaultModelBinder qui est livré avec MVC 5 est assez intelligent pour coopérer avec des attributs de liaison modèle personnalisé.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top