2010-01-08 8 views
6

Ich habe einige Experimente mit ASP.NET MVC2 ausgeführt und bin auf ein interessantes Problem gestoßen.DataAnnotation Validierungen und Custom ModelBinder

Ich möchte eine Schnittstelle um die Objekte definieren, die als Modelle in der MVC-App verwendet werden. Außerdem möchte ich die neue DataAnnotation funktionell nutzen, indem ich die Mitglieder dieser Schnittstelle mit Validierungsattributen auszeichne.

Also, wenn meine Seite ein "Foto" Objekt hat, werde ich die folgende Schnittstelle definieren:

public interface IPhoto 
{ 
[Required] 
string Name { get; set; } 

[Required] 
string Path { get; set; } 
} 

Und ich werde die folgende Implementierung definieren:

public class PhotoImpl : IPhoto 
{ 
public string Name { get; set; } 
public string Path { get; set; } 
} 

Meine MVC App Controller könnten gehören Methoden wie:

public class PhotoController : Controller 
{ 
[HttpGet] 
public ActionResult CreatePhoto() 
{ 
    return View(); 
} 

[HttpPost] 
public ActionResult CreatePhoto(IPhoto photo) 
{ 
    if(ModelState.IsValid) 
    { 
    return View(); 
    } 
    else 
    { 
    return View(photo); 
    } 

} 
} 

Und schließlich, um PhotoImpls auf die Parameter in dieser ac zu binden tion Methoden, könnte ich die folgenden Erweiterungen die Default implementieren:

public class PhotoModelBinder : DefaultModelBinder 
{ 
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
    if(bindingContext.ModelType == typeof(IPhoto)) 
    { 
    IPhoto photo = new PhotoImpl(); 
    // snip: set properties of photo to bound values 
    return photo; 
    } 

    return base.BindModel(controllerContext, bindingContext); 
} 
} 

Alles scheint groß zu arbeiten, mit der Ausnahme, dass die ModelState.IsValid Eigenschaft in meinem Controller erscheint nicht ungültige Werte (zum Beispiel null) zu bemerken, in die Eigenschaften [Erforderlich] der IPhoto-Implementierung.

Ich vermute, dass ich vernachlässige, um ein wichtiges Stück Staat in meiner ModelBinder-Implementierung zu setzen. Irgendwelche Hinweise?

Antwort

7

Nach der Überprüfung der Quelle für System.Web.MVC.DefaultModelBinder sieht es so aus, als ob dies mit einem etwas anderen Ansatz gelöst werden kann. Wenn wir uns mehr auf die Basisimplementierung von BindModel verlassen, sieht es so aus, als ob wir ein PhotoImpl-Objekt erstellen können, während die Validierungsattribute weiterhin von IPhoto übernommen werden.

Etwas wie:

public class PhotoModelBinder : DefaultModelBinder 
{ 
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
     if (bindingContext.ModelType == typeof(IPhoto)) 
     { 
      ModelBindingContext newBindingContext = new ModelBindingContext() 
      { 
       ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(
        () => new PhotoImpl(), // construct a PhotoImpl object, 
        typeof(IPhoto)   // using the IPhoto metadata 
       ), 
       ModelState = bindingContext.ModelState, 
       ValueProvider = bindingContext.ValueProvider 
      }; 

      // call the default model binder this new binding context 
      return base.BindModel(controllerContext, newBindingContext); 
     } 
     else 
     { 
      return base.BindModel(controllerContext, bindingContext); 
     } 
    } 
} 
0

Haben Sie versucht, das Attribut [Erforderlich] auf Ihrem Modell zu platzieren und erneut zu testen? Es kann Schwierigkeiten beim Anwenden des Attributs auf eine Schnittstelle geben.

+0

Vielen Dank für die Antwort. Es scheint kein Problem mit der Position des Attributs [Erforderlich] zu sein. Das Problem wird fortgesetzt, wenn ich die Attribute in PhotoImpl verschiebe, den Controller für die Verwendung in PhotoImpl ändere und den ModelBinder so umschalte, dass er auf Anfragen für PhotoImpl reagiert. Umgekehrt funktionieren die Validierungen korrekt, wenn ich meinem ModelBinder befehle, nicht auf PhotoImpl zu reagieren und auf die standardmäßige ModelBinder-Implementierung zurückzugreifen. –

8

hatte ich das gleiche Problem. Die Antwort ist, anstelle des überwiegenden BindModel() in Ihrem benutzerdefinierten Modell Binder, außer Kraft setzen Create() ...

protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, System.Type modelType) 
{ 
    if (modelType == typeof(IPhoto)) 
    { 
     IPhoto photo = new PhotoImpl(); 
     // snip: set properties of photo to bound values 
     return photo; 
    } 

    return base.CreateModel(controllerContext, bindingContext, modelType); 
} 

Sie können dann die Basis BindModel Klasse können bei der Validierung seiner Sachen zu tun :-)

+0

Ich bin auf der Suche nach einer Lösung für die Bindung mit einem komplexen ViewModel mit DataAnnotations und das ist perfekt, danke! – daddywoodland