2011-11-29 29 views
9

tôi có các thiết lập sau đây trong mô hình của tôi:Thừa kế mô hình có thể khi sử dụng chế độ xem được nhập mạnh mẽ trong MVC3?

namespace QuickTest.Models 
{ 
    public class Person 
    { 
     [Required] 
     [Display(Name = "Full name")] 
     public string FullName { get; set; } 

     [Display(Name = "Address Line 1")] 
     public virtual string Address1 { get; set; } 
    } 
    public class Sender : Person 
    { 
     [Required] 
     public override string Address1 { get; set; } 
    } 
    public class Receiver : Person 
    { 
    } 
} 

và theo quan điểm của tôi:

@model QuickTest.Models.Person 
@{ 
    ViewBag.Title = "Edit"; 
} 
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script> 
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script> 

@using (Html.BeginForm()) { 
    <fieldset> 
     <legend>Person</legend> 
     <div class="editor-label"> 
      @Html.LabelFor(model => model.FullName) 
     </div> 
     <div class="editor-field"> 
      @Html.EditorFor(model => model.FullName) 
      @Html.ValidationMessageFor(model => model.FullName) 
     </div> 
     <div class="editor-label"> 
      @Html.LabelFor(model => model.Address1) 
     </div> 
     <div class="editor-field"> 
      @Html.EditorFor(model => model.Address1) 
      @Html.ValidationMessageFor(model => model.Address1) 
     </div> 

     <div class="errors"> 
      @Html.ValidationSummary(true) 
     </div> 
     <p> 
      <input type="submit" value="Save" /> 
     </p> 
    </fieldset> 
} 

Client-side validation được kích hoạt. Tuy nhiên, nếu tôi gửi một đối tượng kiểu Người gửi đến Chế độ xem, xác thực phía máy khách không phát hiện thấy trường Address1 là bắt buộc. Có cách nào để thực hiện xác thực ứng dụng khách trong kịch bản này không?

PS: tôi phát hiện ra rằng khách hàng xác nhận hoạt động nếu tôi sử dụng sau đây để hiển thị trường Address1 trong giao diện:

<div class="editor-field"> 
    @Html.Editor("Address1", Model.Address1) 
    @Html.ValidationMessageFor(model => model.Address1) 
</div> 
+0

Tên người gửi là người, nhưng người đó không phải là người gửi, chế độ xem của bạn được nhập mạnh vào người, do đó, nó sẽ không bao giờ phát hiện bất kỳ điều gì liên quan đến người gửi. – Maess

+0

Trên thực tế nếu bạn thêm điều này vào khung nhìn: Model.GetType() ToString() bạn sẽ thấy rằng sau đây được hiển thị: QuickTest.Models.Sender có nghĩa là loại được biết khi khung nhìn được hiển thị. – pacu

+0

Tuy nhiên, EditorFor() sẽ xử lý là loại bạn đã gõ mạnh, đó là người. – Maess

Trả lời

8

Bạn có thể tùy chỉnh trình xác thực và siêu dữ liệu đến từ lớp bê tông, nhưng giải pháp có một số phần chuyển động, bao gồm hai nhà cung cấp siêu dữ liệu tùy chỉnh.

Đầu tiên, tạo một tùy chỉnh Attribute để trang trí từng thuộc tính của lớp cơ sở. Điều này là cần thiết như một lá cờ cho các nhà cung cấp tùy chỉnh của chúng tôi, để chỉ ra khi cần phân tích thêm. Đây là thuộc tính:

[AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = true)] 
public class BaseTypeAttribute : Attribute { } 

Tiếp theo, tạo một phong tục ModelMetadataProvider kế thừa từ DataAnnotationsModelMetadataProvider:

public class MyModelMetadataProvider : DataAnnotationsModelMetadataProvider 
{ 
    protected override ModelMetadata CreateMetadata(
     IEnumerable<Attribute> attributes, 
     Type containerType, 
     Func<object> modelAccessor, 
     Type modelType, 
     string propertyName) 
    { 
     var attribute = attributes.FirstOrDefault(a => a.GetType().Equals(typeof(BaseTypeAttribute))) as BaseTypeAttribute; 
     if (attribute != null && modelAccessor != null) 
     { 
      var target = modelAccessor.Target; 
      var containerField = target.GetType().GetField("container"); 
      if (containerField == null) 
      { 
       var vdi = target.GetType().GetField("vdi").GetValue(target) as ViewDataInfo; 
       var concreteType = vdi.Container.GetType(); 
       return base.CreateMetadata(attributes, concreteType, modelAccessor, modelType, propertyName); 
      } 
      else 
      { 
       var container = containerField.GetValue(target); 
       var concreteType = container.GetType(); 
       var propertyField = target.GetType().GetField("property"); 
       if (propertyField == null) 
       { 
        concreteType = base.GetMetadataForProperties(container, containerType) 
         .FirstOrDefault(p => p.PropertyName == "ConcreteType").Model as System.Type; 
        if (concreteType != null) 
         return base.GetMetadataForProperties(container, concreteType) 
          .FirstOrDefault(pr => pr.PropertyName == propertyName); 
       } 
       return base.CreateMetadata(attributes, concreteType, modelAccessor, modelType, propertyName); 
      } 
     } 
     return base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName); 
    } 
} 

Sau đó, tạo một phong tục ModelValidatorProvider kế thừa từ DataAnnotationsModelValidatorProvider:

public class MyModelMetadataValidatorProvider : DataAnnotationsModelValidatorProvider 
{ 
    protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes) 
    { 
     List<ModelValidator> vals = base.GetValidators(metadata, context, attributes).ToList(); 

     var baseTypeAttribute = attributes.FirstOrDefault(a => a.GetType().Equals(typeof(BaseTypeAttribute))) 
      as BaseTypeAttribute; 

     if (baseTypeAttribute != null) 
     { 
      // get our parent model 
      var parentMetaData = ModelMetadataProviders.Current.GetMetadataForProperties(context.Controller.ViewData.Model, 
       metadata.ContainerType); 

      // get the concrete type 
      var concreteType = parentMetaData.FirstOrDefault(p => p.PropertyName == "ConcreteType").Model; 
      if (concreteType != null) 
      { 
       var concreteMetadata = ModelMetadataProviders.Current.GetMetadataForProperties(context.Controller.ViewData.Model, 
        Type.GetType(concreteType.ToString())); 

       var concretePropertyMetadata = concreteMetadata.FirstOrDefault(p => p.PropertyName == metadata.PropertyName); 

       vals = base.GetValidators(concretePropertyMetadata, context, attributes).ToList(); 
      } 
     } 
     return vals.AsEnumerable(); 
    } 
} 

Sau đó, đăng ký cả nhà cung cấp tùy chỉnh trong Application_Start trong Global.asax.cs:

ModelValidatorProviders.Providers.Clear(); 
ModelValidatorProviders.Providers.Add(new MvcApplication8.Controllers.MyModelMetadataValidatorProvider()); 
ModelMetadataProviders.Current = new MvcApplication8.Controllers.MyModelMetadataProvider(); 

Bây giờ, thay đổi mô hình của bạn như sau:

public class Person 
{ 
    public Type ConcreteType { get; set; } 

    [Required] 
    [Display(Name = "Full name")] 
    [BaseType] 
    public string FullName { get; set; } 

    [Display(Name = "Address Line 1")] 
    [BaseType] 
    public virtual string Address1 { get; set; } 
} 

public class Sender : Person 
{ 
    public Sender() 
    { 
     this.ConcreteType = typeof(Sender); 
    } 

    [Required] 
    [Display(Name = "Address Line One")] 
    public override string Address1 { get; set; } 
} 

public class Receiver : Person 
{ 
} 

Lưu ý rằng các lớp cơ sở có một tài sản mới, ConcreteType. Điều này sẽ được sử dụng để chỉ ra lớp kế thừa nào đã khởi tạo lớp cơ sở này. Bất cứ khi nào một lớp kế thừa có siêu dữ liệu ghi đè siêu dữ liệu trong lớp cơ sở, hàm tạo của lớp kế thừa sẽ thiết lập thuộc tính ConcreteType lớp cơ sở.

Bây giờ, mặc dù chế độ xem của bạn sử dụng lớp cơ sở, các thuộc tính cụ thể cho bất kỳ lớp kế thừa cụ thể nào sẽ xuất hiện trong chế độ xem của bạn và sẽ ảnh hưởng đến việc xác thực mô hình.

Ngoài ra, bạn sẽ có thể biến Chế độ xem thành mẫu cho loại Người và sử dụng mẫu cho bất kỳ cá thể nào sử dụng lớp cơ sở hoặc kế thừa từ mẫu đó.

+0

var propertyField = target.GetType(). GetField ("thuộc tính"); Dòng này là gì? – Sergey

+0

Và điều gì dành cho target.GetType(). GetField ("vdi") – Sergey

1

Hmm, đây là một trong những khó khăn kể từ khi phương pháp HtmlHelper<T>.EditorFor sử dụng các tham số chung của HtmlHelper<T> để tìm ra thuộc tính xác thực nào được yêu cầu.

Tôi khuyên bạn nên viết phương thức mở rộng EditorFor của riêng bạn để ủy quyền các cuộc gọi đến phương thức HtmlHelper.Editor không chung chung.

0

Bạn đã cân nhắc tạo EditorTemplate của riêng mình cho Người, Người gửi và Người nhận chưa? EditorFor và DisplayFor tìm mẫu tùy chỉnh khớp với loại đối tượng.

Phương thức nội bộ sẽ tìm mẫu phù hợp với loại đối tượng. Sau đó nó sẽ tìm một mẫu phù hợp với lớp cơ sở và sau đó trên chuỗi thừa kế.

+1

Có, nhưng trong trường hợp của tôi, sự khác biệt giữa Người gửi và Người nhận không đáng kể, vì vậy tôi không nghĩ rằng có một điểm trong việc có cùng một cái nhìn hai lần chỉ vì mục đích thay đổi chỉ thị @model ở đầu nó. – pacu

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