2012-03-20 40 views
7

我試圖讓ASP.NET MVC 3從複雜的嵌套對象中生成表單。有一種驗證行爲,我發現這是意外的,我不確定它是否是DefaultModelBinder中的錯誤。關於嵌套對象的ASP.NET MVC 3驗證不能按預期方式工作 - 驗證子對象兩次而不是父對象

如果我有兩個對象,讓我們稱之爲 「父」 一個 「OuterObject」,它有一個類型的屬性 「InnerObject」(孩子):

public class OuterObject : IValidatableObject 
{ 
    [Required] 
    public string OuterObjectName { get; set; } 

    public InnerObject FirstInnerObject { get; set; } 

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) 
    { 
     if (!string.IsNullOrWhiteSpace(OuterObjectName) && string.Equals(OuterObjectName, "test", StringComparison.CurrentCultureIgnoreCase)) 
     { 
      yield return new ValidationResult("OuterObjectName must not be 'test'", new[] { "OuterObjectName" }); 
     } 
    } 
} 

這裏是InnerObject:

public class InnerObject : IValidatableObject 
{ 
    [Required] 
    public string InnerObjectName { get; set; } 

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) 
    { 
     if (!string.IsNullOrWhiteSpace(InnerObjectName) && string.Equals(InnerObjectName, "test", StringComparison.CurrentCultureIgnoreCase)) 
     { 
      yield return new ValidationResult("InnerObjectName must not be 'test'", new[] { "InnerObjectName" }); 
     } 
    } 
} 

你會注意到我對兩者都進行的驗證..只是一些虛擬驗證說一些價值不能等於「測試」。

這裏是,這將顯示(Index.cshtml)的觀點:終於在這裏

@model MvcNestedObjectTest.Models.OuterObject 
@{ 
    ViewBag.Title = "Home Page"; 
} 

@using (Html.BeginForm()) { 
<div> 
    <fieldset> 
     <legend>Using "For" Lambda</legend> 

     <div class="editor-label"> 
      @Html.LabelFor(m => m.OuterObjectName) 
     </div> 
     <div class="editor-field"> 
      @Html.TextBoxFor(m => m.OuterObjectName) 
      @Html.ValidationMessageFor(m => m.OuterObjectName) 
     </div> 

     <div class="editor-label"> 
      @Html.LabelFor(m => m.FirstInnerObject.InnerObjectName) 
     </div> 
     <div class="editor-field"> 
      @Html.TextBoxFor(m => m.FirstInnerObject.InnerObjectName) 
      @Html.ValidationMessageFor(m => m.FirstInnerObject.InnerObjectName) 
     </div> 

     <p> 
      <input type="submit" value="Test Submit" /> 
     </p> 
    </fieldset> 
</div> 
} 

..和是HomeController的:

public class HomeController : Controller 
{ 
    public ActionResult Index() 
    { 
     var model = new OuterObject(); 
     model.FirstInnerObject = new InnerObject(); 
     return View(model); 
    } 

    [HttpPost] 
    public ActionResult Index(OuterObject model) 
    { 
     if (ModelState.IsValid) 
     { 
      return RedirectToAction("Index"); 
     } 
     return View(model); 
    } 
} 

你會發現是,當模型被DefaultModelBinder驗證,「InnerObject」中的「Validate」方法被擊兩次,但「OuterObject」中的「Validate」方法根本沒有被擊中。

如果從「InnerObject」中取下IValidatableObject,則「OuterObject」上的那個將會被擊中。

這是一個錯誤,或者我應該期望它的工作方式?如果我期望它,什麼是最好的解決方法?

回答

0

如果您已經爲InnerObject創建了OuterObject基類,而不是像以前那樣創建關係? (反之亦然),並提供視圖的基礎對象作爲ViewModel?

這將意味着當模型綁定OuterObject的默認構造函數(或者哪個永遠是你的基類)時,將間接調用這兩個對象的Validate。

即 類:

public class OuterObject : InnerObject, IValidateableObject 
{ 
... 
} 

查看:

@model MvcNestedObjectTest.Models.OuterObject 

控制器動作:

public ActionResult Index(OuterObject model) 
+0

感謝我曾想過這一點,並會工作了這種特殊的情況,但作爲德對象變得更復雜,它不會工作。例如,如果我需要InnerObject1,SomeString,InnerObject2,SomeOtherString(即其他屬性之間的嵌套對象).. – nootn 2012-03-20 21:01:57

+0

@nootn您是否嘗試過[流暢驗證](http:// http://fluentvalidation.codeplex.com/wikipage?title = mvc&referTitle = Documentation)這是驗證依賴性和嵌套驗證規則的高級方法嗎? – amythn04 2012-03-22 07:52:32

+0

我已經研究過它,它看起來不錯,但在我們的組織中,我們選擇使用內置的數據註釋作爲驗證MVC中的模型的標準。我想我們可以使用一個組合,但它仍然沒有解決這個問題,並不像預期的那樣,這有點陷阱。 – nootn 2012-03-23 06:27:58

1

這個答案只是提供一個解決方法我剛纔想到的 - 所以它並不是真正的答案!我仍然不確定這是一個錯誤還是最好的解決方法,但這裏有一個選擇。

如果您從「InnerObject」中刪除自定義驗證邏輯並將其合併到「OuterObject」中,它似乎工作正常。所以基本上這只是通過只允許最頂層的對象進行自定義驗證來解決這個問題。

這是新InnerObject:

//NOTE: have taken IValidatableObject off as this causes the issue - we must remember to validate it manually in the "Parent"! 
public class InnerObject //: IValidatableObject 
{ 
    [Required] 
    public string InnerObjectName { get; set; } 
} 

這裏是新OuterObject(從InnerObject被盜輸入驗證碼):

public class OuterObject : IValidatableObject 
{ 
    [Required] 
    public string OuterObjectName { get; set; } 

    public InnerObject FirstInnerObject { get; set; } 

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) 
    { 
     if (!string.IsNullOrWhiteSpace(OuterObjectName) && string.Equals(OuterObjectName, "test", StringComparison.CurrentCultureIgnoreCase)) 
     { 
      yield return new ValidationResult("OuterObjectName must not be 'test'", new[] { "OuterObjectName" }); 
     } 

     if (FirstInnerObject != null) 
     { 
      if (!string.IsNullOrWhiteSpace(FirstInnerObject.InnerObjectName) && 
       string.Equals(FirstInnerObject.InnerObjectName, "test", StringComparison.CurrentCultureIgnoreCase)) 
      { 
       yield return new ValidationResult("InnerObjectName must not be 'test'", new[] { "FirstInnerObject.InnerObjectName" }); 
      } 
     } 
    } 
} 

這個工程,我所期望的,鉤住了驗證錯誤正確地對每個字段。

這不是一個好的解決方案,因爲如果我需要在其他類中嵌套「InnerObject」,它不會共享該驗證 - 我需要複製它。很明顯,我可以在類上有一個方法來存儲邏輯,但每個「父」類都需要記住「驗證」子類。

1

我不知道這是MVC 4問題了,但是......

如果你只使用你的InnerObjects做局部的意見,他們會正確地驗證。

<fieldset> 
    <legend>Using "For" Lambda</legend> 

    <div class="editor-label"> 
     @Html.LabelFor(m => m.OuterObjectName) 
    </div> 
    <div class="editor-field"> 
     @Html.TextBoxFor(m => m.OuterObjectName) 
     @Html.ValidationMessageFor(m => m.OuterObjectName) 
    </div> 

    @Html.Partial("_InnerObject", Model.InnerObject) 

    <p> 
     <input type="submit" value="Test Submit" /> 
    </p> 
</fieldset> 

然後添加該部分「_InnerObject.cshtml」:

@model InnerObject 

    <div class="editor-label"> 
     @Html.LabelFor(m => m.InnerObjectName) 
    </div> 
    <div class="editor-field"> 
     @Html.TextBoxFor(m => m.InnerObjectName) 
     @Html.ValidationMessageFor(m => m.InnerObjectName) 
    </div>