Laden...

ASP.NET MVC 2: Model Binding mit [Bind]

Erstellt von m0rius vor 13 Jahren Letzter Beitrag vor 13 Jahren 1.354 Views
m0rius Themenstarter:in
1.002 Beiträge seit 2007
vor 13 Jahren
ASP.NET MVC 2: Model Binding mit [Bind]

Hallo,

um zu vermeiden, dass beim Model Binding von ASP.NET MVC 2 per [Bind] Eigenschaften gebunden werden, die nicht gebunden werden sollen, schränke ich diese wie folgt ein:

[MetadataType(typeof(Dinner_Validation))]
[Bind(Include = "Title,Description,EventDate,Address,Country,ContactPhone,Latitude,Longitude")]
public partial class Dinner
{
    /* ... */
}

Der Nachteil ist, dass die Eigenschaften hier nicht umbenannt werden, sobald ich diese an einer anderen Stelle per Refactor => Rename... umbenenne. Das führt zu schwer aufzufindenden Bugs, wenn sich bestimmte Formularfelder nicht mehr editieren lassen, weil die zugehörige Eigenschaft umbenannt wurde.

Gibt es – neben Alternativen wie ViewModels etc. – für diese Art der Beschränkung eine Lösung für das Problem?

m0rius

Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg

5.941 Beiträge seit 2005
vor 13 Jahren

Salute m0rius

Wieso machst du kein "Exclude"?
Also Black- anstelle von Whitelisting.

Gruss Peter

--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

m0rius Themenstarter:in
1.002 Beiträge seit 2007
vor 13 Jahren

Hallo Peter Bucher,

weil dadurch mein Problem weiterhin besteht. So würde ich mir eher noch eine zusätzliche Sicherheitslücke (Tampering) einholen, wenn ich beispielsweise die Eigenschaft Id vom Binding ausschließe, die in einem Refactoring zu DinnerID umbenannt wird.

m0rius

Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg

1.373 Beiträge seit 2004
vor 13 Jahren

Hi m0rius,

Getrennte ViewModels haben natürlich Vor- und Nachteile, siehe hier: http://geekswithblogs.net/michelotti/archive/2009/10/25/asp.net-mvc-view-model-patterns.aspx

Wenn du keine getrennten ViewModels haben willst, hier eine Alternative: Erstelle eine Basisklasse für Dinner (z.B. BindableDinner), die alle Eigenschaften enthält, die du binden möchtest. Im Controller benutzt du dann TryUpdateModel oder UpdateModel wie folgt:


public ActionResult MeineAction(Dinner input){
  Dinner data =  /* aus DB oder ähnlich */
  
  // nur die Properties von BindableDinner binden:
  if(TryUpdateModel<BindableDinner>(data)){
     // alles ok
  } 
}

Damit kannst du ohne Strings und refactoringsicher Attribute vom Binding ausschließen.

Das ganze geht statt mit einer Basisklasse auch mit interfaces. Details siehe hier: http://www.dotnetcurry.com/ShowArticle.aspx?ID=439

Statt UpdateModel könntest du auch BindableDinner als Parameter akzeptieren, das Model validieren und dann das Mapping zum echten Model mittels AutoMapper vornehmen.


public ActionResult MeineAction(BindableDinner input){

  Dinner data = /* aus DB oder ähnlich */
  if(ModelState.IsValid){
    Mapper.Map(input, data);
    // alles klar!
  }
}

Hierbei muss Dinner natürlich nicht mal von BindableDinner erben, womit wir quasi wieder beim ViewModel wären.

Noch eine Alternative, ohne BindableDinner: du erstellst die Liste der zu inkludierenden Properties mittels Lambaausdrücken und gibst diese Liste an UpdateModel weiter:


public class Dinner {
  public static readonly string[] BindableProperties;

  static Dinner(){
    BindableProperties= new []{
     PropertyName<Dinner>(x=>x.Title),
     PropertyName<Dinner>(x=>x.Description), 
    // usw.
    };
  }

  static string PropertyName<T>(Expression<Func<T, object>> expr) {
    var member = expr.Body as MemberExpression;
    return member.Member.Name;
  }
}

// 

public ActionResult MeineAction(){
  Dinner dinner = /* aus DB */
  if(TryUpdateModel(dinner, Dinner.BindableProperties)){
    // okidoki
  }
  ...
}
  

Das ist ebenfalls 100%ig refactoringsicher.

So viel zu meinen Vorschlägen. Aber wie gesagt, ein reines ViewModel ist auch eine Alternative, wenn auch mit den bekannten Problemen (v.a. Code-Duplikation).

Grüße,
Andre

m0rius Themenstarter:in
1.002 Beiträge seit 2007
vor 13 Jahren

Hallo VizOne,

vielen Dank für deine ausführliche Antwort! Ich werde wahrscheinlich die Binding-Basisklasse umsetzen, da mir die Gefahr bei mehreren Entwicklern zu hoch ist, dass vergessen wird, die Include-Liste anzupassen.

m0rius

Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg

1.373 Beiträge seit 2004
vor 13 Jahren

Natürlich gibt es noch eine Möglichkeit, die Implementierung eines eigenen IModelBinder, ggf. auf Basis des DefaultModelBinders. Damit kannst du das dann vollkommen transparent machen.

Idealerweise führst du dafür ein Attribut ein (DontBindAttribute oder ähnlich), dass dein Modelbinder dann verwendet, um Eigenschaften zu ignorieren. Überhaupt merkwürdig, dass das noch existiert (oder habe ich was übersehen?).