2012-03-20 16 views
7

Tôi đang cố gắng lấy ASP.NET MVC 3 để tạo biểu mẫu từ các đối tượng lồng nhau phức tạp. Có một hành vi xác thực mà tôi thấy là không mong muốn và tôi không chắc đó có phải là lỗi trong DefaultModelBinder hay không.Xác thực ASP.NET MVC 3 trên đối tượng lồng nhau không hoạt động như mong đợi - xác nhận đối tượng con hai lần và không đối tượng cha mẹ

Nếu tôi có hai đối tượng, cho phép gọi là "mẹ" một "OuterObject", và nó có một loại tài sản của "InnerObject" (đứa trẻ):

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

Đây là 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" }); 
     } 
    } 
} 

Bạn sẽ nhận thấy xác thực tôi đặt trên cả hai .. chỉ cần một số xác thực giả để nói một số giá trị không thể bằng "kiểm tra".

Đây là quan điểm cho rằng điều này sẽ hiển thị trong (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> 
} 

..và cuối cùng ở đây là 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); 
    } 
} 

gì bạn sẽ tìm thấy là khi mô hình được xác nhận bởi DefaultModelBinder, phương thức "Validate" trong "InnerObject" được nhấn hai lần, nhưng phương thức "Validate" trong "OuterObject" không bị ảnh hưởng gì cả.

Nếu bạn tắt IValidatableObject từ "InnerObject", thì phần tử trên "OuterObject" sẽ bị trúng.

Đây có phải là lỗi hay tôi có mong đợi nó hoạt động theo cách đó không? Nếu tôi mong đợi nó, giải pháp tốt nhất là gì?

Trả lời

0

Nếu bạn đã tạo lớp cơ sở OuterObject cho InnerObject thay vì tạo mối quan hệ như bạn đã làm? (Hoặc ngược lại) và cung cấp khung nhìn đối tượng cơ sở như ViewModel?

Điều này có nghĩa là khi mô hình ràng buộc hàm khởi tạo mặc định của OuterObject (hoặc lớp nào là cơ sở của bạn) sẽ được gọi gián tiếp gọi Validate trên cả hai đối tượng.

tức Class:

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

Xem:

@model MvcNestedObjectTest.Models.OuterObject 

điều khiển hành động:

public ActionResult Index(OuterObject model) 
+0

cám ơn tôi đã nghĩ về điều đó và nó sẽ làm việc cho tình huống đặc biệt này, nhưng như teh đối tượng trở nên phức tạp hơn nó sẽ không hoạt động. Ví dụ: nếu tôi cần InnerObject1, SomeString, InnerObject2, SomeOtherString (Tức là đối tượng lồng nhau giữa các thuộc tính khác) .. – nootn

+0

@nootn Bạn đã thử [Xác thực thông thạo] (http: // http: //fluentvalidation.codeplex.com chưa/wikipage? title = mvc & referringTitle = Tài liệu) là một cách nâng cao để xác thực các phụ thuộc và các quy tắc xác nhận lồng nhau? – amythn04

+0

Tôi đã xem xét điều đó và nó có vẻ tốt đẹp, nhưng trong tổ chức của chúng tôi, chúng tôi đã chọn để đi với các chú thích dữ liệu được xây dựng như là một tiêu chuẩn để xác nhận các mô hình trong MVC. Tôi đoán chúng tôi có thể sử dụng một sự kết hợp, nhưng nó vẫn không nhận được xung quanh thực tế rằng điều này không hoạt động như mong đợi, nó là một chút của một cái bẫy. – nootn

1

Câu trả lời này chỉ là để cung cấp một workaround Tôi vừa nghĩ - vì vậy nó không thực sự là một câu trả lời! Tôi vẫn không chắc chắn nếu đây là một lỗi hay giải pháp tốt nhất là gì, nhưng đây là một lựa chọn.

Nếu bạn xóa logic xác thực tùy chỉnh khỏi "InnerObject" và kết hợp nó thành "OuterObject", có vẻ như nó hoạt động tốt. Vì vậy, về cơ bản, nó hoạt động xung quanh lỗi bằng cách chỉ cho phép đối tượng cao nhất có bất kỳ xác nhận tùy chỉnh nào.

Đây là InnerObject mới:

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

Và đây là OuterObject mới (với mã Validation bị đánh cắp từ 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" }); 
      } 
     } 
    } 
} 

này hoạt động như tôi mong đợi, hooking lên lỗi xác thực cho từng trường một cách chính xác.

Nó không phải là một giải pháp tuyệt vời bởi vì nếu tôi cần phải tổ "InnerObject" trong một số lớp khác, nó không chia sẻ xác nhận đó - tôi cần phải nhân rộng nó. Rõ ràng tôi có thể có một phương thức trên lớp để lưu trữ logic, nhưng mỗi lớp "cha mẹ" cần phải nhớ "Xác thực" lớp con.

1

Tôi không chắc chắn đây là một vấn đề với MVC 4 nữa, nhưng ...

Nếu bạn sử dụng quang cảnh một phần thực hiện chỉ cho InnerObjects của bạn, họ sẽ xác nhận một cách chính xác.

<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> 

Sau đó thêm phần "_InnerObject.cshtml" này:

@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> 
Các vấn đề liên quan