数据注释验证和自定义模型绑定器

时间:2010-01-08 19:17:07

标签: asp.net-mvc asp.net-mvc-2

我一直在使用ASP.NET MVC2进行一些实验,并遇到了一个有趣的问题。

我想在MVC应用程序中定义将用作模型的对象周围的接口。另外,我想通过使用验证属性标记此接口的成员来在功能上利用新的DataAnnotation。

因此,如果我的网站有“Photo”对象,我将定义以下界面:

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

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

我将定义以下实现:

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

我的MVC App控制器可能包含以下方法:

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

 }
}

最后,为了将PhotoImpls绑定到这些操作方法中的参数,我可能会对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);
 }
}

除了我的控制器中的ModelState.IsValid属性似乎没有注意到IPhoto实现的[Required]属性中的无效值(例如,null)之外,所有内容似乎都运行良好。

我怀疑我忽略了在我的ModelBinder实现中设置一些重要的状态。任何提示?

3 个答案:

答案 0 :(得分:9)

我有同样的问题。答案是在自定义模型绑定器中覆盖BindModel(),而不是覆盖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);
}

然后你可以让基础BindModel类通过验证来完成它的工作: - )

答案 1 :(得分:7)

在检查System.Web.MVC.DefaultModelBinder的源代码之后,看起来这可以使用稍微不同的方法来解决。如果我们更依赖于BindModel的基本实现,看起来我们可以构建一个PhotoImpl对象,同时仍然从IPhoto中提取验证属性。

类似的东西:

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

答案 2 :(得分:0)

您是否尝试在模型上放置[必需]属性并重新测试?将属性应用于接口可能有困难。