2012-02-23 38 views
44

Giả sử tôi có một mô hình sản phẩm, mô hình sản phẩm có một thuộc tính ProductSubType (trừu tượng) và chúng tôi có hai cách triển khai cụ thể là áo sơ mi và quần.Mô hình MVC 3 Ràng buộc một loại phụ (Lớp trừu tượng hoặc giao diện)

Dưới đây là nguồn:

public class Product 
{ 
    public int Id { get; set; } 

    [Required] 
    public string Name { get; set; } 

    [Required] 
    public decimal? Price { get; set; } 

    [Required] 
    public int? ProductType { get; set; } 

    public ProductTypeBase SubProduct { get; set; } 
} 

public abstract class ProductTypeBase { } 

public class Shirt : ProductTypeBase 
{ 
    [Required] 
    public string Color { get; set; } 
    public bool HasSleeves { get; set; } 
} 

public class Pants : ProductTypeBase 
{ 
    [Required] 
    public string Color { get; set; } 
    [Required] 
    public string Size { get; set; } 
} 

Trong giao diện người dùng của tôi, người dùng có một thả xuống, họ có thể chọn các loại sản phẩm và các yếu tố đầu vào sẽ được hiển thị theo loại sản phẩm thích hợp. Tôi có tất cả điều này được tìm ra (bằng cách sử dụng ajax nhận được trên thay đổi thả xuống, trả về một phần/trình soạn thảo mẫu và tái thiết lập xác nhận jquery cho phù hợp).

Tiếp theo, tôi đã tạo một trình kết nối mô hình tùy chỉnh cho ProductTypeBase.

public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
{ 

     ProductTypeBase subType = null; 

     var productType = (int)bindingContext.ValueProvider.GetValue("ProductType").ConvertTo(typeof(int)); 

     if (productType == 1) 
     { 
      var shirt = new Shirt(); 

      shirt.Color = (string)bindingContext.ValueProvider.GetValue("SubProduct.Color").ConvertTo(typeof(string)); 
      shirt.HasSleeves = (bool)bindingContext.ValueProvider.GetValue("SubProduct.HasSleeves").ConvertTo(typeof(bool)); 

      subType = shirt; 
     } 
     else if (productType == 2) 
     { 
      var pants = new Pants(); 

      pants.Size = (string)bindingContext.ValueProvider.GetValue("SubProduct.Size").ConvertTo(typeof(string)); 
      pants.Color = (string)bindingContext.ValueProvider.GetValue("SubProduct.Color").ConvertTo(typeof(string)); 

      subType = pants; 
     } 

     return subType; 

    } 
} 

Điều này ràng buộc các giá trị chính xác và hoạt động cho hầu hết các phần, ngoại trừ tôi mất xác thực phía máy chủ. Vì vậy, trên một linh cảm rằng tôi đang làm này không chính xác tôi đã làm một số tìm kiếm hơn và đã xem qua câu trả lời này bởi Darin Dimitrov:

ASP.NET MVC 2 - Binding To Abstract Model

Vì vậy, tôi chuyển sang mô hình chất kết dính để chỉ ghi đè CreateModel, nhưng bây giờ nó không ràng buộc các giá trị.

protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) 
    { 
     ProductTypeBase subType = null; 

     var productType = (int)bindingContext.ValueProvider.GetValue("ProductType").ConvertTo(typeof(int)); 

     if (productType == 1) 
     { 
      subType = new Shirt(); 
     } 
     else if (productType == 2) 
     { 
      subType = new Pants(); 
     } 

     return subType; 
    } 

Đẩy mạnh dù MVC 3 src, nó có vẻ như trong BindProperties, các GetFilteredModelProperties trả về một kết quả có sản phẩm nào, và tôi nghĩ là vì mô hình BindingContext được thiết lập để ProductTypeBase mà không có bất cứ tài sản.

Có ai có thể phát hiện ra những gì tôi đang làm sai? Điều này dường như không phải là khó khăn như vậy. Tôi chắc chắn tôi thiếu một cái gì đó đơn giản ... Tôi có một thay thế trong tâm trí thay vì có một tài sản SubProduct trong mô hình sản phẩm để chỉ có các thuộc tính riêng biệt cho áo sơ mi và quần. Đây chỉ là các mô hình Xem/Biểu mẫu nên tôi nghĩ điều đó sẽ hiệu quả, nhưng muốn có cách tiếp cận hiện tại đang hoạt động nếu có bất cứ điều gì để hiểu điều gì đang xảy ra ...

Cảm ơn sự giúp đỡ của bạn!

Cập nhật:

Tôi không làm cho nó rõ ràng, nhưng mô hình tùy chỉnh chất kết dính Tôi nói thêm, được thừa hưởng từ DefaultModelBinder

trả lời

Thiết ModelMetadata và Model là phần còn thiếu. Cảm ơn Manas!

protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) 
     { 
      if (modelType.Equals(typeof(ProductTypeBase))) { 
       Type instantiationType = null; 

       var productType = (int)bindingContext.ValueProvider.GetValue("ProductType").ConvertTo(typeof(int)); 

       if (productType == 1) { 
        instantiationType = typeof(Shirt); 
       } 
       else if (productType == 2) { 
        instantiationType = typeof(Pants); 
       } 

       var obj = Activator.CreateInstance(instantiationType); 
       bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, instantiationType); 
       bindingContext.ModelMetadata.Model = obj; 
       return obj; 
      } 

      return base.CreateModel(controllerContext, bindingContext, modelType); 

     } 

Trả lời

53

Điều này có thể đạt được thông qua việc ghi đè CreateModel (...). Tôi sẽ chứng minh điều đó với một ví dụ.

1. Cho phép tạo mô hình và một số lớp cơ sở và trẻ em.

public class MyModel 
{ 
    public MyBaseClass BaseClass { get; set; } 
} 

public abstract class MyBaseClass 
{ 
    public virtual string MyName 
    { 
     get 
     { 
      return "MyBaseClass"; 
     } 
    } 
} 

public class MyDerievedClass : MyBaseClass 
{ 

    public int MyProperty { get; set; } 
    public override string MyName 
    { 
     get 
     { 
      return "MyDerievedClass"; 
     } 
    } 
} 

2. Bây giờ, tạo một ModelBinder và ghi đè CreateModel

public class MyModelBinder : DefaultModelBinder 
{ 
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) 
    { 
     /// MyBaseClass and MyDerievedClass are hardcoded. 
     /// We can use reflection to read the assembly and get concrete types of any base type 
     if (modelType.Equals(typeof(MyBaseClass))) 
     { 
      Type instantiationType = typeof(MyDerievedClass);     
      var obj=Activator.CreateInstance(instantiationType); 
      bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, instantiationType); 
      bindingContext.ModelMetadata.Model = obj; 
      return obj; 
     } 
     return base.CreateModel(controllerContext, bindingContext, modelType); 
    } 

} 

3. Bây giờ trong bộ điều khiển tạo ra được và sau khi hành động.

[HttpGet] 
public ActionResult Index() 
    { 
     ViewBag.Message = "Welcome to ASP.NET MVC!"; 

     MyModel model = new MyModel(); 
     model.BaseClass = new MyDerievedClass(); 

     return View(model); 
    } 

    [HttpPost] 
    public ActionResult Index(MyModel model) 
    { 

     return View(model); 
    } 

4. Bây giờ Set as Default MyModelBinder ModelBinder trong global.asax này được thực hiện để thiết lập một mô hình mặc định chất kết dính cho tất cả các hành động, đối với một hành động đơn lẻ chúng ta có thể sử dụng thuộc tính ModelBinder trong các thông số hành động)

protected void Application_Start() 
    { 
     AreaRegistration.RegisterAllAreas(); 

     ModelBinders.Binders.DefaultBinder = new MyModelBinder(); 

     RegisterGlobalFilters(GlobalFilters.Filters); 
     RegisterRoutes(RouteTable.Routes); 
    } 

5. Bây giờ chúng ta có thể tạo ra quan điểm của loại MyModel và nhìn ra một góc loại MyDerievedClass

Index.cshtml

@model MvcApplication2.Models.MyModel 

@{ 
ViewBag.Title = "Index"; 
Layout = "~/Views/Shared/_Layout.cshtml"; 
} 

<h2>Index</h2> 

@using (Html.BeginForm()) { 
@Html.ValidationSummary(true) 
<fieldset> 
    <legend>MyModel</legend> 
    @Html.EditorFor(m=>m.BaseClass,"DerievedView") 
    <p> 
     <input type="submit" value="Create" /> 
    </p> 
</fieldset> 
} 

DerievedView.cshtml

@model MvcApplication2.Models.MyDerievedClass 

@Html.ValidationSummary(true) 
<fieldset> 
    <legend>MyDerievedClass</legend> 

    <div class="editor-label"> 
     @Html.LabelFor(model => model.MyProperty) 
    </div> 
    <div class="editor-field"> 
     @Html.EditorFor(model => model.MyProperty) 
     @Html.ValidationMessageFor(model => model.MyProperty) 
    </div> 

</fieldset> 

Bây giờ nó sẽ làm việc như mong đợi, điều khiển sẽ nhận được một đối tượng của kiểu "MyDerievedClass". Xác thực sẽ diễn ra như mong đợi.

enter image description here

+2

Hoàn hảo, đặt ModelMetaData và Mô hình tạo ra là phần bị thiếu, cảm ơn! –

+0

Tôi đã có một vấn đề rất giống với một loại thừa hưởng và có nguồn gốc và mã mô hình ở trên đã làm các trick. Chúc mừng! –

+0

Cảm ơn! Tôi dành gần ba ngày để thử những thứ khác nhau để cuối cùng sử dụng giải pháp này, mặc dù vấn đề của tôi có phần khác biệt. –

4

tôi đã cùng một vấn đề, tôi đã kết thúc bằng MvcContrib như sugested here.

documentation đã lỗi thời nhưng nếu bạn nhìn vào các mẫu, nó khá dễ dàng.

Bạn sẽ phải đăng ký loại của bạn trong Global.asax:

protected void Application_Start(object sender, EventArgs e) { 
    // (...) 
    DerivedTypeModelBinderCache.RegisterDerivedTypes(typeof(ProductTypeBase), new[] { typeof(Shirt), typeof(Pants) }); 
} 

Thêm hai dòng quang cảnh một phần của bạn:

@model MvcApplication.Models.Shirt 
@using MvcContrib.UI.DerivedTypeModelBinder 
@Html.TypeStamp() 
<div> 
    @Html.LabelFor(m => m.Color) 
</div> 
<div> 
    @Html.EditorFor(m => m.Color) 
    @Html.ValidationMessageFor(m => m.Color) 
</div> 

Cuối cùng, trong giao diện chính (sử dụng EditorTemplates):

@model MvcApplication.Models.Product 
@{ 
    ViewBag.Title = "Products"; 
} 
<h2> 
    @ViewBag.Title</h2> 

@using (Html.BeginForm()) { 
    <div> 
     @Html.LabelFor(m => m.Name) 
    </div> 
    <div> 
     @Html.EditorFor(m => m.Name) 
     @Html.ValidationMessageFor(m => m.Name) 
    </div> 
    <div> 
     @Html.EditorFor(m => m.SubProduct) 
    </div> 
    <p> 
     <input type="submit" value="create" /> 
    </p> 
} 
1

well Tôi đã có cùng một vấn đề này và tôi đã giải quyết một cách tổng quát hơn tôi nghĩ. Trong trường hợp của tôi tôi gửi đối tượng qua Json từ backend cho khách hàng và từ khách hàng để phụ trợ:

Trước hết Trong lớp trừu tượng tôi có lĩnh vực mà tôi đặt trong xây dựng:

ClassDescriptor = this.GetType().AssemblyQualifiedName; 

So In Json I Have lĩnh vực ClassDescriptor

sau đó là viết tùy chỉnh chất kết dính:

public class SmartClassBinder : DefaultModelBinder 
{ 
     protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) 
     { 

      string field = String.Join(".", new String[]{bindingContext.ModelName , "ClassDescriptor"}); 
       var values = (ValueProviderCollection) bindingContext.ValueProvider; 
       var classDescription = (string) values.GetValue(field).ConvertTo(typeof (string)); 
       modelType = Type.GetType(classDescription); 

      return base.CreateModel(controllerContext, bindingContext, modelType); 
     }  
} 

Và bây giờ tất cả tôi phải làm là để trang trí lớp học với ít cống vật. Ví dụ:

[ModelBinder (typeof (SmartClassBinder))] public class ConfigurationItemDescription

Vậy là xong.

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