简体   繁体   中英

DataAnnotation Validations and Custom ModelBinder

I've been running some experiments with ASP.NET MVC2 and have run into an interesting problem.

I'd like to define an interface around the objects that will be used as Models in the MVC app. Additionally, I'd like to take advantage of the new DataAnnotation functionally by marking up the members of this interface with validation attributes.

So, if my site has a "Photo" object, I'll define the following interface:

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

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

And I'll define the following implementation:

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

My MVC App controller might include methods like:

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

 }
}

And finally, in order to bind PhotoImpls to the parameters in these action methods, I might implement the following extensions to the 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);
 }
}

Everything appears to working great, except that the ModelState.IsValid property in my controller doesn't appear to notice invalid values (say, null) in the [Required] properties of the IPhoto implementation.

I suspect that I'm neglecting to set some important piece of state in my ModelBinder implementation. Any hints?

I had the same issue. The answer is instead of overriding BindModel() in your custom model binder, override 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);
}

You can then let the base BindModel class do its stuff with validation :-)

After inspecting the source for System.Web.MVC.DefaultModelBinder, it looks like this can be solved using a slightly different approach. If we rely more heavily on the base implementation of BindModel, it looks like we can construct a PhotoImpl object while still pulling the validation attributes from IPhoto.

Something like:

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

Have you tried placing the [Required] attribute on your model and retesting? It may be having difficulty applying the attribute to an interface.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM