2010-01-08 24 views
6

He estado realizando algunos experimentos con ASP.NET MVC2 y me he encontrado con un problema interesante.Validaciones de la anotación de datos y Custom ModelBinder

Me gustaría definir una interfaz alrededor de los objetos que se utilizarán como Modelos en la aplicación MVC. Además, me gustaría aprovechar funcionalmente la nueva DataAnnotation marcando los miembros de esta interfaz con atributos de validación.

lo tanto, si mi sitio tiene un objeto "Foto", voy a definir la interfaz siguiente:

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

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

Y voy a definir la siguiente implementación:

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

Mi MVC App El controlador puede incluir métodos como:

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

} 
} 

Y, por último, para vincular PhotoImpls a los parámetros en estos ac métodos ción, que podrían llevar a cabo las siguientes ampliaciones de la DefaultModelBinder:

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

Todo parece trabajar muy bien, excepto que la propiedad ModelState.IsValid en mi controlador no parece darse cuenta de valores no válidos (por ejemplo, null) en las propiedades [Requerido] de la implementación de IPhoto.

Sospecho que estoy descuidando establecer alguna pieza importante de estado en mi implementación de ModelBinder. ¿Algún consejo?

Respuesta

7

Después de inspeccionar la fuente de System.Web.MVC.DefaultModelBinder, parece que esto se puede resolver utilizando un enfoque ligeramente diferente. Si confiamos más en la implementación básica de BindModel, parece que podemos construir un objeto PhotoImpl mientras seguimos extrayendo los atributos de validación de IPhoto.

Algo así como:

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

¿Ha intentado colocar el atributo [Requerido] en su modelo y volver a probar? Puede estar teniendo dificultades para aplicar el atributo a una interfaz.

+0

Gracias por la respuesta. No parece ser un problema con la ubicación del atributo [Requerido]. El problema continúa si muevo los atributos a PhotoImpl, cambio el controlador para operar en un PhotoImpl y cambio el ModelBinder para que actúe en las solicitudes de un PhotoImpl. Por el contrario, si le indico a mi ModelBinder que no actúe sobre PhotoImpl y que recurra a la implementación predeterminada de ModelBinder, las validaciones funcionan correctamente. –

8

Yo tenía el mismo problema. La respuesta es en lugar de anular bindModel() en su carpeta de modelos personalizados, anular CreateModel() ...

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

A continuación, puede dejar que la clase bindModel base de hacer sus cosas con la validación :-)

+0

He estado buscando una solución para enlazar con un ViewModel complejo con DataAnnotations y esto es perfecto gracias. – daddywoodland

Cuestiones relacionadas