2013-01-11 19 views
14

Tôi có một mô hình lớn (lớp mô hình có nghĩa là lớn chứa rất nhiều trường/thuộc tính và mỗi trường có ít nhất một thuộc tính xác thực (chẳng hạn như Required, MaxLength, MinLength v.v.)). Thay vì tạo một chế độ xem với nhiều đầu vào để người dùng điền vào mô hình với dữ liệu tôi muốn tạo một số chế độ xem nơi người dùng sẽ điền vào một phần trường mô hình và chuyển sang bước tiếp theo (một số loại "wizard"). Trong khi chuyển hướng giữa các bước, tôi lưu trữ đối tượng mô hình không được điền đầy đủ trong Session. Một cái gì đó như dưới đây:Làm cách nào để xác thực chỉ một phần của mô hình trong ASP .NET MVC?

mẫu:

public class ModelClass 
{ 
    [MaxLength(100)] ... 
    public string Prop1{get;set;} 
    [MaxLength(100)] ... 
    public string Prop2{get;set;} 
    ... 
    [Required][MaxLength(100)] ... 
    public string Prop20{get;set;} 
} 

Bộ điều khiển:

[HttpPost] 
public ActionResult Step1(ModelClass postedModel) 
{  
    // user posts only for example Prop1 and Prop2 
    // so while submit I have completly emty model object 
    // but with filled Prop1 and Prop2 
    // I pass those two values to Session["model"] 
    var originalModel = Session["model"] as ModelClass ?? new ModelClass(); 
    originalModel.Prop1 = postedModel.Prop1; 
    originalModel.Prop2 = postedModel.Prop2; 
    Session["model"] = originalModel; 

    // and return next step view 
    return View("Step2"); 
} 

[HttpPost] 
public ActionResult Step2(ModelClass postedModel) 
{ 
    // Analogically the same 
    // I have posted only Prop3 and Prop4 

    var originalModel = Session["model"] as ModelClass; 
    if (originalModel!=null) 
    { 
     originalModel.Prop3 = postedModel.Prop3; 
     originalModel.Prop4 = postedModel.Prop4; 
     Session["model"] = originalModel; 

     // return next step view 
     return View("Step3"); 
    } 
    return View("SomeErrorViewIfSessionBrokesSomeHow") 
} 

Step1 xem có đầu vào chỉ cho Prop1Prop2, Bước 2 view chứa đầu vào cho Prop3Prop4, vv

NHƯNG Ở ĐÂY LÀ ĐIỀU KHOẢN

Khi người dùng đang bật, ví dụ: bước 1 và điền vào Prop1 có giá trị hơn 100 ký tự, xác thực phía máy khách dài hoạt động tốt. Nhưng, tất nhiên, tôi phải xác nhận giá trị này và ở phía máy chủ trong bộ điều khiển. Nếu tôi có mô hình đầy đủ, tôi chỉ cần làm như sau:

if(!ModelState.IsValid) return View("the same view with the same model object"); 

để người dùng phải điền lại biểu mẫu và sửa lại. NHƯNG trên người dùng bước 1 chỉ điền 2 thuộc tính của 20 và tôi cần xác thực chúng. Tôi không thể sử dụng ModelState.IsValid vì trạng thái mô hình sẽ không hợp lệ. Như bạn có thể thấy Prop20 được đánh dấu bằng thuộc tính [Required], khi người dùng gửi Prop1Prop2, Prop20 là không và đó là lý do tại sao ModelState không hợp lệ. Tất nhiên tôi có thể cho phép người dùng chuyển sang bước 2, điền vào tất cả các bước và xác thực trạng thái mô hình chỉ ở bước cuối cùng nhưng tôi không muốn cho phép người dùng chuyển sang bước 2 nếu anh ta điền vào bước 1 không chính xác. Và tôi muốn xác nhận này trong bộ điều khiển. Vì vậy, câu hỏi là: Làm cách nào tôi có thể xác thực chỉ một phần của mô hình? Làm cách nào tôi có thể xác minh rằng chỉ một số thuộc tính mô hình khớp với thuộc tính xác thực của chúng?

+8

Bạn có thể tạo mô hình chế độ xem khác nhau cho mỗi bước có thể là chế độ xem riêng biệt. Giống như ProductStep1ViewModel và ProductStep1View, nhưng tôi sẽ đặt tên cho chúng tốt hơn thế. –

+0

@NickBray, ModelClass là lớp mô hình, được ánh xạ từ mô hình ban đầu, vì vậy nếu tôi tạo ModelClass làm thành phần của các lớp bước cụ thể khác, tôi sẽ phải thêm khoảng 40 quy tắc ánh xạ cho trình ánh xạ để ánh xạ các lớp mô hình đó, và nó sẽ là một nhóm của mã. Tôi đã nghĩ về điều đó, nhưng cảm ơn lời khuyên. – Dmytro

+0

@DmytroTsiniavsky Bạn có thể sử dụng một cái gì đó như [Value Injecter] (http://valueinjecter.codeplex.com/) để tránh phải thiết lập quy tắc ánh xạ một cách rõ ràng. – Mun

Trả lời

14

Một giải pháp khả thi:

  1. Sử dụng ModelState.IsValidField (khóa chuỗi);

    if (ModelState.IsValidField("Name") && ModelState.IsValidField("Address")) 
    { ... } 
    

Sau đó, ở cuối khi tất cả mọi thứ được thực hiện sử dụng:

if(ModelState.IsValid) { .. } 
+0

Nếu bạn điền vào một chuỗi không excisting nó wil luôn luôn trở lại, hãy chắc chắn rằng bạn gõ nó một cách chính xác. –

9

Tôi nghĩ rằng cách thanh lịch nhất là để làm điều đó như thế:

List<string> PropertyNames = new List<string>() 
{ 
    "Prop1", 
    "Prop2" 
}; 

if (PropertyNames.Any(p => !ModelState.IsValidField(p))) 
{ 
    // Error 
} 
else 
{ 
    // Everything is okay 
} 

hoặc :

List<string> PropertyNames = new List<string>() 
{ 
    "Prop1", 
    "Prop2" 
}; 

if (PropertyNames.All(p => ModelState.IsValidField(p))) 
{ 
    // Everything is okay 
} 
else 
{ 
    // Error 
} 
2

Chỉ để thêm vào các câu trả lời hiện có cho việc này. Thay vì thể xác định rõ tên thuộc tính Tôi sẽ sử dụng một thuộc tính được thêm vào cùng với phần còn lại của xác nhận của bạn thuộc tính dọc theo dòng:

public class ValidationStageAttribute : Attribute 
{ 
    public StageNumber { get; private set; } 
    public ValidationStageAttribute(int stageNumber) 
    { 
     StageNumber = stageNumber 
    } 
} 

Bây giờ chúng ta có thể nhận được các tên thuộc tính mà không cần kiến ​​thức về mô hình chính nó partial xác nhận có thể được kéo vào một phương pháp (nếu bạn sử dụng nó rất nhiều, bộ điều khiển cơ sở của bạn sẽ là một điểm tốt).

protected bool ValidateStage(object model, int stageToValidate) 
{ 
    var propertiesForStage = postedModel.GetType() 
     .GetProperties() 
     .Where(prop => prop.GetCustomAttributes(false).OfType<ValidationStageAttribute>().Where(attr => attr.StageNumber == stageToValidate)); 
     .Select(prop => prop.Name); 

    return propertiesForStage.All(p => ModelStage.IsValidField(p)); 
} 

Bây giờ tất cả các bạn sẽ cần phải làm trong bài hành động của bạn sẽ được gọi ValidateStage(postedModel, 1)

2

Trong MVC Core, đây sẽ là tương đương với:

if (ModelState.GetFieldValidationState("Name") == Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Valid) 
{ 
    // do something 
} 

Tuy nhiên, tôi sẽ khuyên bạn chỉ cần tạo một kiểu xem riêng biệt trong trường hợp này.

Mô hình xem một phần của bạn có thể được kế thừa bởi mô hình chế độ xem lớn hơn của bạn, do đó bạn sẽ không phải lặp lại chính mình trong mã (DRY chính).

Tốt hơn là bạn nên tránh mã hóa cứng tên thuộc tính!