【问题标题】:DataAnnotation Validations and Custom ModelBinderDataAnnotation 验证和自定义 ModelBinder
【发布时间】:2010-01-08 19:17:07
【问题描述】:

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

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

所以,如果我的网站有一个“照片”对象,我将定义以下接口:

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 应用控制器可能包含如下方法:

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 实现中设置一些重要的状态。有什么提示吗?

【问题讨论】:

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


    【解决方案1】:

    我有同样的问题。答案是不要在自定义模型绑定器中覆盖 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 类通过验证来完成它的工作:-)

    【讨论】:

    • 我一直在寻找使用 DataAnnotations 与复杂 ViewModel 绑定的解决方案,非常感谢!
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 感谢您的回答。 [Required] 属性的位置似乎不是问题。如果我将属性移动到 PhotoImpl,将 Controller 更改为对 PhotoImpl 进行操作,并将 ModelBinder 更改为对 PhotoImpl 的请求进行操作,问题仍然存在。相反,如果我指示我的 ModelBinder 不对 PhotoImpl 执行操作并回退到默认的 ModelBinder 实现,则验证功能正确。
      猜你喜欢
      • 2013-04-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多