2010-01-08 77 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