Frage

Ich habe folgende Controller-Aktion:

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

Wo MyModel sieht wie folgt aus:

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

So Default sollte diese binden, ohne ein Problem. Die einzige Sache ist, dass ich spezielles / custom Bindemittel zum Binden PropertyB und ich möchte auch die Wiederverwendung dieses Bindemittel verwendet werden soll. Also habe ich diese Lösung gedacht, wäre ein Modelbinder-Attribut zu setzen, bevor die PropertyB was natürlich nicht funktioniert (Modelbinder-Attribut auf einem Eigenschaft nicht erlaubt ist). Ich sehe zwei Lösungen:

  1. Aktionsparameter verwenden, um auf jede einzelne Eigenschaft anstelle des gesamten Modells (was ich nicht als Modell bevorzugen hat viele Eigenschaften) wie folgt aus:

    public ViewResult DoSomething(string propertyA, [ModelBinder(typeof(MyModelBinder))] propertyB)
    
  2. eine neue Art erstellen kann sagen, MyCustomType: List<int> und Binder Modell für diese Art registrieren (dies ist eine Option)

  3. Vielleicht ein Bindemittel für MyModel gekröpft BindProperty zu erstellen und wenn die Eigenschaft "PropertyB" bindet die Eigenschaft mit meinen benutzerdefinierten Bindern. Ist das möglich?

Gibt es eine andere Lösung?

War es hilfreich?

Lösung

  

außer Kraft setzen bindProperty und wenn die   Eigentum ist „PropertyB“ binden die   Immobilien mit meinem benutzerdefinierten Binder

Das ist eine gute Lösung, wenn anstelle der Prüfung „ist PropertyB“ Sie besser überprüfen Sie Ihre eigenen benutzerdefinierten Attribute, die Eigenschaft Ebene Bindemittel definieren, wie

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

Sie können ein Beispiel für bindProperty Überschreibung siehe hier .

Andere Tipps

Eigentlich mag ich Ihre dritte Lösung, nur würde ich es eine generische Lösung für alle Modelbinder macht, indem sie sie in einem benutzerdefinierten Bindemittel, das erbt von DefaultModelBinder setzen und so konfiguriert ist, das Standardmodell Bindemittel für Ihre MVC-Anwendung zu sein.

Dann würden Sie diese neue DefaultModelBinder automatisch machen bindet jede Eigenschaft, die mit einem PropertyBinder-Attribute versehen ist, die Art mit in dem Parameter geliefert.

Ich habe die Idee von diesem ausgezeichneten Artikel: http://aboutcode.net/2011/ 12.03 / mvc-Eigenschafts-binder.html .

Ich werde Sie auch auf die Lösung mein nehmen zeigen:

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

Meine IPropertyBinder Schnittstelle:

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

Mein PropertyBinderAttribute:

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

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

Ein Beispiel für eine Eigenschaft Bindemittel:

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

Beispiel der oben genannten Eigenschaft Bindemittel verwendet werden:

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

@ jonathanconway Antwort ist groß, aber ich möchte ein kleines Detail hinzuzufügen.

Es ist wahrscheinlich besser, die GetPropertyValue Methode statt BindProperty um außer Kraft zu setzen, den Validierungsmechanismus des DefaultBinder eine Chance, Arbeit zu geben.

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

Es ist 6 Jahre her, seit diese Frage gestellt wurde, würde ich lieber diesen Raum nehmen das Update zusammenzufassen, anstatt eine völlig neue Lösung bereitzustellen. Zum Zeitpunkt des Schreibens, 5 MVC wurde für eine ganze Weile um, und ASP.NET-Core hat gerade aus.

Ich folgte der Ansatz in der Post geprüft geschrieben von Vijaya Anand (btw, dank Vijaya): http://www.prideparrot.com/blog/archive/2012/6/customizing_property_binding_through_attributes . Und eine Sache, erwähnenswert ist, dass die Datenbindung Logik in der benutzerdefinierten Attributklasse platziert wird, die die bindProperty Methode der StringArrayPropertyBindAttribute Klasse in Vijaya Anand Beispiel.

Doch in allen anderen Artikel zu diesem Thema, dass ich gelesen habe (einschließlich @ jonathanconway-Lösung), benutzerdefinierte Attributklasse ist nur ein Schritt Stein, führt das Framework den richtigen benutzerdefinierten Modell Binder, um herauszufinden, anzuwenden; und die Bindung Logik wird in diesem kundenspezifischen Modell Bindemittel platziert, die in der Regel ein IModelBinder Objekt.

Der erste Ansatz ist mir einfacher. Möglicherweise gibt es einige Mängel des ersten Ansatz sein, dass ich noch nicht kenne, aber Coz ich im Moment zu MVC-Framework ziemlich neu bin.

Darüber hinaus fand ich, dass die ExtendedModelBinder Klasse in Vijaya Anand Beispiel in MVC 5. unnötig ist, es scheint, dass die Default Klasse, die mit MVC kommt 5 ist intelligent genug, um mit benutzerdefinierter Modell Bindung Attributen zu kooperieren.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top