2010-01-08 79 views
6

我一直在使用ASP.NET MVC2运行一些实验,并遇到了一个有趣的问题。DataAnnotation验证和自定义ModelBinder

我想在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绑定到这些ac中的参数重刑方法,我可能会实现以下扩展到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财产似乎并没有注意到无效值(比方说,NULL)在IPhoto实现的[必需]属性。

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

回答

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

你有没有尝试在你的模型上放置[Required]属性并重新测试?将该属性应用于界面可能有困难。

+0

感谢答案。 它似乎不是[必需的]属性的位置问题。如果我将属性移动到PhotoImpl,将控制器更改为在PhotoImpl上操作,并将ModelBinder更改为处理PhotoImpl的请求,问题就会继续。相反,如果我指示我的ModelBinder不对PhotoImpl起作用并回退到默认的ModelBinder实现,则验证功能可以正常工作。 – 2010-01-11 16:25:00

8

我有同样的问题。答案是不是在您的自定义模型绑定重写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类做它的东西与验证:-)

+0

我一直在寻找一种解决方案来绑定与DataAnnotations一个复杂的ViewModel,这是完美的感谢! – daddywoodland 2010-09-01 11:47:00