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 ViewModel
s 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
Salute m0rius
Wieso machst du kein "Exclude"?
Also Black- anstelle von Whitelisting.
Gruss Peter
--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011
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
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
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
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?).