2012-07-04 15 views
6

Đây là vấn đề rất giống với một đã được đăng ở đây: ASP.NET MVC: Validation messages set in TryUpdateModel not showning ValidationSummaryMVC ValidationSummary bỏ qua lỗi xác nhận mức độ mô hình khi bị ràng buộc sử dụng TryUpdateModel

Tôi không chắc chắn cho dù đó là chủ đề cũ là trong tham chiếu đến một phiên bản trước của MVC, nhưng trong MVC3 tôi đang trải qua một số hành vi kỳ lạ dọc theo các dòng tương tự.

Tôi có lớp mô hình được gọi là Trade. Điều này kế thừa từ IValidatableObject, do đó, do đó thực hiện một phương thức Validate. Trong vòng này, chúng tôi có một số xác nhận của mô hình như một toàn thể (như trái ngược với chú thích dữ liệu thực thi xác nhận các thuộc tính). Việc xác thực là như sau:

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) 
    { 
    var validationResults = new List<ValidationResult>(); 

    if (this.EndDate < this.StartDate) 
    { 
     validationResults.Add(new ValidationResult("End date must be greater than start date")); 
    } 

    return validationResults; 
    } 

Chúng tôi có một mô hình xem để giúp hiển thị giao dịch. Điều này chứa một tham chiếu đến một mô hình thương mại thông qua một tài sản TradeModel. Vì vậy, về cơ bản mô hình xem là một mô hình Thương mại, cộng thêm một số thông tin bổ sung cho dân số danh sách thả xuống như Đối tác, v.v.

Lớp CSHTML của chúng tôi chứa một ValidationSummary, với "true" làm đối số, có nghĩa là nó sẽ chỉ hiển thị lỗi mô hình.

Nếu tôi thực hiện phương pháp điều khiển HttpPost của tôi để tạo ra một thương mới như sau ...

[HttpPost] 
    public ActionResult Create(FormCollection collection) 
    { 
    var trade = new Trade(); 

    if (this.TryUpdateModel(trade)) 
    { 
     if (this.SaveChanges(this.ModelState, trade)) 
     { 
      return this.RedirectToAction("Index"); 
     } 
    } 

    return this.View(trade); 
    } 

... khi tôi bước vào một thương mại với StartDate> EndDate, tôi thấy rằng TryUpdateModel trả về false và người dùng được quay trở lại giao dịch của họ. Điều này có vẻ hợp lý. Thật không may ValidationSummary không hiển thị bất kỳ thông báo lỗi nào.

Nếu tôi đặt điểm ngắt trong phương thức Tạo và điều tra ModelState, tôi có thể thấy rằng có thông báo lỗi trong từ điển. Nó là chống lại một khóa của "TradeModel", chứ không phải là chống lại bất kỳ tài sản. Một lần nữa, điều này có vẻ hợp lý. Một lý thuyết về lý do tại sao điều này là, ValidationSummary giả định rằng bất kỳ lỗi xác thực nào đối với khóa không phải là String.Empty phải là lỗi xác thực thuộc tính, nó bỏ qua lỗi xác thực của chúng tôi bởi vì chúng tôi có một mô hình xem có chứa tham chiếu với một mô hình, do đó dẫn đến khóa là "TradeModel".

gì thổi lý thuyết này ra khỏi nước là thế này: nếu tôi viết lại chức năng Tạo của bộ điều khiển như sau ...

[HttpPost] 
    public ActionResult Create(Trade trade, FormCollection collection) 
    { 
    if (this.SaveChanges(this.ModelState, trade)) 
    { 
     return this.RedirectToAction("Index"); 
    } 

    return this.View(trade); 
    } 

... và do đó dựa trên MVC thực hiện các ràng buộc "tự động", và chạy lại cùng một kịch bản thử nghiệm, người dùng được trình bày với thông báo lỗi dự định!

Nếu tôi thêm điểm ngắt và xem ModelState, tôi thấy thông báo lỗi giống hệt với các phím giống như trước, nhưng lần này ValidationSummary chọn chúng!

Nếu tôi sửa đổi các xác nhận như sau sau đó nó hoạt động với Tạo chức năng của bộ điều khiển viết bằng một trong hai cách:

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) 
    { 
    var validationResults = new List<ValidationResult>(); 

    if (this.EndDate < this.StartDate) 
    { 
     validationResults.Add(new ValidationResult("End date must be greater than start date", new[] { "StartDate" })); 
    } 

    return validationResults; 
    } 

Vì vậy, rõ ràng nó chỉ là một vấn đề với lỗi xác nhận mức độ mô hình.

Bất kỳ trợ giúp nào về điều này sẽ được đánh giá rất nhiều! Có những lý do (mà tôi sẽ không đi vào bây giờ) tại sao chúng ta cần tạo cá thể của mô hình khung nhìn của chúng ta một cách thủ công và gọi ràng buộc bằng cách sử dụng TryUpdateModel.

Cảm ơn trước!

CẬP NHẬT

Dường như lý thuyết về ValidationSummary chỉ hiển thị lỗi với một chìa khóa trong ModelState của String.Empty khi nói để loại trừ các lỗi bất động sản thực sự là sự thật. Tôi đã nhìn vào mã nguồn và nó thực sự sử dụng ViewData.TemplateInfo.HtmlFieldPrefix để tìm các lỗi xác nhận ở mức mô hình. Theo mặc định, đây là String.Empty.

Thay đổi giá trị này thành "TradeModel" do đó có vẻ hợp lý, nhưng nó làm cho mọi id hoặc tên HTML được đặt trước, do đó ràng buộc không thành công!

Nếu mô hình khung chứa tham chiếu đến mô hình kinh doanh, mọi lỗi được thêm vào ModelState bởi IValidatableObject được thêm bằng khóa bao gồm tiền tố bằng tên thuộc tính mô hình kinh doanh trong mô hình chế độ xem (trong trường hợp của chúng tôi là "TradeModel"), dẫn đến các khóa chẳng hạn như "TradeModel.CounterpartyId", v.v. Lỗi cấp mô hình được thêm bằng khóa bằng với tên thuộc tính mô hình kinh doanh của mô hình chế độ xem ("TradeModel").

Vì vậy, có vẻ như doanh nghiệp không thể thêm lỗi xác thực cấp mô hình nếu mô hình chế độ xem được tạo theo cách này.

Điều khiến tôi khó hiểu là tại sao điều này thực sự hoạt động trong dự án "thực" của chúng tôi khi hàm Create của bộ điều khiển được viết sao cho đối tượng mô hình dạng xem là đối số. Tôi phải đã bỏ lỡ ngày hôm qua, nhưng nhìn vào nó ngày hôm nay, khi MVC liên kết và xác nhận được kích hoạt, nó xuất hiện để thêm một khóa bổ sung ở cuối từ điển với một giá trị của String.Empty. Điều này chứa một bản sao của các lỗi được thêm bằng một khóa của TradeModel. Như bạn mong đợi, ValidationSummary sau đó chọn chúng lên!

Vậy tại sao MVC thực hiện điều này trong dự án trực tiếp của chúng tôi chứ không phải trong ứng dụng thử nghiệm đơn giản?

Tôi đã thấy xác thực được kích hoạt hai lần khi chức năng bộ điều khiển được viết để đưa mô hình xem làm đối số. Có lẽ điều này đang làm một cái gì đó tinh tế khác nhau mỗi lần?

CẬP NHẬT ... LẠI

Lý do nó hoạt động trong dự án thực của chúng tôi là có một số mã chôn trong bộ điều khiển cơ sở của chúng tôi (mà từ đó tất cả những người khác kế thừa) cho phép sao chép tất cả các lỗi được tìm thấy trong tình trạng mô hình để một mục mới với khóa của String.Empty. Mã này chỉ được gọi trong một trong hai kịch bản. Vì vậy, không có sự khác biệt thực sự giữa các ứng dụng thực và thử nghiệm.

Tôi hiện đã hiểu những gì đang diễn ra và tại sao ValidationSummary hoạt động như thế này.

+0

Tôi đã cố gắng repro vấn đề của bạn mà không thành công. Đối với tôi trong một dự án MVC3 mới, hành động thứ hai ** của bạn ** 'Public ActionResult Create (Trade trade, FormCollection collection)' cũng ** không hiển thị ** thông báo xác thực như mong đợi. Bạn có thể tự tạo bản thân trong một dự án trống để xác minh hành vi và có thể tải lên ở đâu đó không? – nemesv

+0

Thú vị. Cảm ơn vì đã cố gắng tái tạo nó. Tôi sẽ tạo một ví dụ đơn giản trong một dự án mới như bạn đề xuất. –

+0

Tôi đã tạo một dự án đơn giản và, như bạn đã nói, nó không hoạt động với cả hai phiên bản của chức năng Tạo của bộ điều khiển. –

Trả lời

5

Ok. Bây giờ tôi đang ở một điểm mà tôi có thể tự mình trả lời.

Nếu ValidationSummary được sử dụng với đối số ExcludePropertyErrors được đặt thành True, nó sẽ tìm lỗi trong trạng thái mô hình bằng cách sử dụng khóa bằng ViewData.TemplateInfo.HtmlFieldPrefix. Theo mặc định, đây là String.Empty.

Nếu bạn có một mô hình xem mà thấy nhiều mô hình kinh doanh của bạn, một cái gì đó như thế này:

namespace ValidationSummary.Models 
{ 
    using System; 
    using System.Collections.Generic; 
    using System.ComponentModel.DataAnnotations; 

    public class TradeModel : IValidatableObject 
    { 
     public DateTime StartDate { get; set; } 

     public DateTime EndDate { get; set; } 

     public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) 
     { 
     List<ValidationResult> validationResults = new List<ValidationResult>(); 

     if (EndDate < StartDate) 
     { 
      validationResults.Add(new ValidationResult("End date must not be before start date")); 
     } 

     return validationResults; 
     } 
    } 
} 

namespace ValidationSummary.ViewModels 
{ 
    public class Trade 
    { 
     public Trade() 
     { 
     this.TradeModel = new Models.TradeModel(); 
     } 

     public Models.TradeModel TradeModel { get; private set; } 
    } 
} 

Khi xác nhận xảy ra, lỗi (ValidationResults) được thêm vào mô hình cấp (nơi không có thêm đối số cho hàm tạo ValidationResult lấy tên thuộc tính, được thêm vào ModelState với tiền tố của tên thuộc tính của mô hình khung nhìn - trong ví dụ này là "TradeModel".

Có một vài cách mà chúng tôi hiện đang xem xét.

  1. Không hiển thị mô hình kinh doanh bên ngoài mô hình chế độ xem. Về cơ bản, điều này liên quan đến việc gắn kết với mô hình khung nhìn, chứ không phải là mô hình kinh doanh của mô hình khung nhìn. Điều này có thể được thực hiện theo hai cách: làm cho mô hình xem là một phân lớp của mô hình kinh doanh; hoặc các thuộc tính trùng lặp từ mô hình kinh doanh sang mô hình khung nhìn. Tôi thích cái cũ hơn.
  2. Viết mã để sao chép lỗi cấp mô hình vào khóa từ điển ModelState mới của String.Empty. Thật không may, điều này có thể không được thực hiện một cách tao nhã vì tên thuộc tính của mô hình khung hiển thị mô hình kinh doanh là những gì được sử dụng làm khóa. Điều này có thể khác nhau trên mỗi bộ điều khiển/chế độ xem.
  3. Sử dụng các mục sau trong chế độ xem. Điều này sẽ hiển thị thông báo lỗi dành cho mô hình kinh doanh. Về cơ bản, điều này giả vờ rằng các lỗi ở mức mô hình cho mô hình kinh doanh là trong các lỗi thuộc tính thực tế. Màn hình trong số này là không giống như đối với ValidationSummary, nhưng có lẽ điều này có thể được chữa khỏi với CSS:

    @ Html.ValidationMessageFor (m => m.TradeModel)

  4. Subclass ValidationSummary. Điều này sẽ liên quan đến việc thay đổi nó để nó biết được các khóa nào trong ModelState tham chiếu đến các thuộc tính mô hình nghiệp vụ của mô hình khung nhìn.

Các vấn đề liên quan