2010-12-10 29 views
9

Trong ứng dụng web ASP.NET MVC 2 của tôi, tôi cho phép người dùng tạo các trường nhập tùy chỉnh của các kiểu dữ liệu khác nhau. Trong khi khó khăn, việc xây dựng biểu mẫu đầu vào từ một tập hợp các trường tùy chỉnh đủ nhanh về phía trước.ASP.NET MVC - Đăng một biểu mẫu với các trường tùy chỉnh của các kiểu dữ liệu khác nhau

Tuy nhiên, tôi bây giờ đến mức tôi muốn xử lý việc đăng biểu mẫu này và tôi không chắc chắn cách tốt nhất để xử lý vấn đề này là gì. Thông thường, chúng tôi sẽ sử dụng các kiểu nhập mạnh mẽ được liên kết từ các đầu vào được nhập tĩnh khác nhau có sẵn trên biểu mẫu. Tuy nhiên, tôi đang thua lỗ về cách thực hiện điều này với một số lượng trường biến thể đại diện cho các loại dữ liệu khác nhau.

Một hình thức đầu vào đại diện có thể giống như thế:

  • trường ngày của tôi: [ngày đầu vào thời gian kiểm soát]
  • lĩnh vực văn bản của tôi: [text input field]
  • tập tin của tôi trường: [tập tin tải lên điều khiển]
  • Trường số của tôi: [điều khiển nhập số]
  • văn bản lĩnh vực của tôi 2: [trường nhập văn bản]
  • vv ...

Ý tưởng Tôi đã nghĩ đến việc là:

  • Gửi tất cả mọi thứ như dây đàn (trừ đầu vào tập tin , cần phải được xử lý đặc biệt).
  • Sử dụng mô hình có thuộc tính "đối tượng" và cố gắng liên kết với điều đó (nếu điều này thậm chí có thể).
  • Gửi yêu cầu json tới bộ điều khiển của tôi với dữ liệu được mã hóa chính xác và cố gắng phân tích cú pháp đó.
  • Thủ công xử lý bộ sưu tập biểu mẫu trong tác vụ bài đăng trên bộ điều khiển của tôi - chắc chắn là một tùy chọn, nhưng tôi muốn tránh điều này.

Có ai đã giải quyết vấn đề như thế này trước đây không? Nếu vậy, làm cách nào bạn giải quyết nó?

Cập nhật:

"cơ sở" hình thức của tôi là xử lý trên một lĩnh vực đầu vào tất cả lại với nhau, do đó, một giải pháp không cần phải giải thích cho bất kỳ loại ma thuật thừa kế cho việc này. Tôi chỉ quan tâm đến việc xử lý các trường tùy chỉnh trên giao diện này, chứ không phải các trường tùy chỉnh "cơ sở" của tôi.

Cập nhật 2:

Cảm ơn bạn ARM và smartcaveman; cả hai bạn đều cung cấp hướng dẫn tốt về cách thực hiện điều này. Tôi sẽ cập nhật câu hỏi này với giải pháp cuối cùng của mình sau khi được triển khai.

Trả lời

1

Đây là cách tôi bắt đầu tiếp cận vấn đề. Một mô hình tùy chỉnh chất kết dính sẽ được khá dễ dàng để xây dựng dựa trên tài sản FormKey (có thể được xác định bởi chỉ số và/hoặc nhãn, tùy thuộc).

public class CustomFormModel 
{ 
    public string FormId { get; set; } 
    public string Label { get; set; } 
    public CustomFieldModel[] Fields { get; set; } 
} 
public class CustomFieldModel 
{ 
    public DataType DateType { get; set; } // System.ComponentModel.DataAnnotations 
    public string FormKey { get; set; } 
    public string Label { get; set; } 
    public object Value { get; set; } 
} 
public class CustomFieldModel<T> : CustomFieldModel 
{ 
    public new T Value { get; set; } 
} 

Ngoài ra, tôi nhận thấy một trong các nhận xét bên dưới có hệ thống kết nối mô hình được lọc. Jimmy Bogard từ Automapper đã thực hiện một bài viết thực sự hữu ích về phương pháp này tại http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/03/17/a-better-model-binder.aspx, và sau đó sửa đổi trong, http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/11/19/a-better-model-binder-addendum.aspx. Nó đã được rất hữu ích cho tôi trong việc xây dựng chất kết dính mô hình tùy chỉnh.

Cập nhật

Tôi nhận ra rằng tôi hiểu sai câu hỏi, và rằng ông đã đặc biệt yêu cầu làm thế nào để xử lý niêm yết dưới hình thức "với một số biến của lĩnh vực đầu vào mà đại diện cho các kiểu dữ liệu khác nhau". Tôi nghĩ cách tốt nhất để làm điều này là sử dụng cấu trúc tương tự như trên nhưng tận dụng số Composite Pattern. Về cơ bản, bạn sẽ cần phải tạo một giao diện như IFormComponent và triển khai nó cho mỗi kiểu dữ liệu sẽ được biểu diễn. Tôi đã viết và cho ý kiến ​​một giao diện ví dụ để giúp giải thích cách thức này sẽ được thực hiện:

public interface IFormComponent 
{ 
    // the id on the html form field. In the case of a composite Id, that doesn't have a corresponding 
    // field you should still use something consistent, since it will be helpful for model binding 
    // (For example, a CompositeDateField appearing as the third field in the form should have an id 
    // something like "frmId_3_date" and its child fields would be "frmId_3_date_day", "frmId_3_date_month", 
    // and "frmId_3_date_year". 
    string FieldId { get; } 

    // the human readable field label 
    string Label { get; } 

    // some functionality may require knowledge of the 
    // Parent component. For example, a DayField with a value of "30" 
    // would need to ask its Parent, a CompositeDateField 
    // for its MonthField's value in order to validate 
    // that the month is not "February" 
    IFormComponent Parent { get; } 

    // Gets any child components or null if the 
    // component is a leaf component (has no children). 
    IList<IFormComponent> GetChildren(); 

    // For leaf components, this method should accept the AttemptedValue from the value provider 
    // during Model Binding, and create the appropriate value. 
    // For composites, the input should be delimited in someway, and this method should parse the 
    // string to create the child components. 
    void BindTo(string value); 

    // This method should parse the Children or Underlying value to the 
    // default used by your business models. (e.g. a CompositeDateField would 
    // return a DateTime. You can get type safety by creating a FormComponent<TValue> 
    // which would help to avoid issues in binding. 
    object GetValue(); 

    // This method would render the field to the http response stream. 
    // This makes it easy to render the forms simply by looping through 
    // the array. Implementations could extend this for using an injected 
    // formatting 
    void Render(TextWriter writer); 
} 

Tôi giả định rằng các hình thức tùy chỉnh có thể được truy cập thông qua một số loại id mà có thể được chứa như một tham số hình thức. Với giả định đó, người mẫu và nhà cung cấp mô hình có thể trông giống như thế này.

public interface IForm : IFormComponent 
{ 
    Guid FormId { get; } 
    void Add(IFormComponent component); 
} 
public interface IFormRepository 
{ 
    IForm GetForm(Guid id); 
} 
public class CustomFormModelBinder : IModelBinder 
{ 
    private readonly IFormRepository _repository; 
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
     ValueProviderResult result; 
     if(bindingContext.ValueProvider.TryGetValue("_customFormId", out result)) 
     { 
      var form = _repository.GetForm(new Guid(result.AttemptedValue)); 
      var fields = form.GetChildren(); 
      // loop through the fields and bind their values 
      return form; 
     } 
     throw new Exception("Form ID not found."); 
    } 
} 

Rõ ràng, tất cả mã ở đây chỉ để lấy điểm và cần được hoàn thành và dọn dẹp để sử dụng thực tế. Ngoài ra, ngay cả khi hoàn thành điều này sẽ chỉ liên kết với một thực hiện của giao diện IForm, không phải là một đối tượng kinh doanh mạnh mẽ đánh máy.(Nó sẽ không phải là một bước tiến lớn để chuyển đổi nó thành từ điển và xây dựng một proxy mạnh mẽ bằng cách sử dụng Castle DictionaryAdapter, nhưng vì người dùng của bạn đang tạo động các biểu mẫu trên trang web, có lẽ không có mô hình nào được đánh máy mạnh mẽ trong giải pháp của bạn và điều này là không liên quan). Hy vọng điều này sẽ giúp ích nhiều hơn.

+0

Cảm ơn các ý kiến, rất sâu sắc. – DanP

1

Xem nhanh những gì tôi đã làm ở đây: MVC2 Action to handle multiple models và xem liệu có thể giúp bạn đi đúng hướng hay không.

Nếu bạn sử dụng FormCollection làm tham số cho hành động của mình, bạn có thể đi qua bộ sưu tập biểu mẫu tìm kiếm các bit dữ liệu ở đây hoặc ở đó để liên kết các giá trị đó với bất kỳ dữ liệu nào. Bạn có nhiều khả năng sẽ cần tận dụng cả chiến lược và mẫu lệnh để làm việc này.

Chúc bạn may mắn, vui lòng đặt câu hỏi tiếp theo.

Edit:

phương pháp của bạn mà không làm việc nên giống như thế này:

private/public void SaveCustomFields(var formId, FormCollection collection) //var as I don't know what type you are using to Id the form. 
{ 
    var binders = this.binders.select(b => b.CanHandle(collection)); //I used IOC to get my list of IBinder objects 
    // Method 1:  
    binders.ForEach(b => b.Save(formId, collection)); //This is the execution implementation. 
    // Method 2: 
    var commands = binders.Select(b => b.Command(formId, collection)); 
    commands.ForEach(c => c.Execute());  
} 

public DateBinder : IBinder //Example binder 
{ 
    public bool CanHandle(FormCollection collection) 
    { 
     return (null != collection["MyDateField"]); //Whatever the name of this field is. 
    } 

    //Method 1 
    public void Save(var formId, FormCollection collection) 
    { 
     var value = DateTime.Parse(collection["MyDateField"]); 
     this.someLogic.Save(formId, value); //Save the value with the formId, or however you wish to save it. 
    } 
    //Method 2 
    public Command Command(var formId, FormCollection collection) 
    { 
     //I haven't done command pattern before so I'm not sure exactly what to do here. 
     //Sorry that I can't help further than that. 
    } 
} 
+0

Cảm ơn thông tin này, cách tiếp cận của bạn trông rất thú vị. Tôi có lẽ sẽ có một số theo dõi cho bạn vào thứ hai. – DanP

+0

ARM; Tôi rất sẵn lòng trao cho bạn tiền thưởng nếu bạn sẵn sàng đăng/chia sẻ chi tiết triển khai có liên quan hơn. – DanP

+0

Phần quan trọng nhất của điều này là IUIWrapper.CanHandle (bạn sẽ muốn sử dụng một lựa chọn thay vì SingleOrDefault để có được nhiều trình bao bọc). Phương thức CanHandle lấy FormCollection và cố gắng lấy một phần tử sưu tập (var X = collection ["SomeValue"]; trả về X! = Null;) sẽ xác định nếu phần tử thu thập biểu mẫu cụ thể tồn tại. Khi bạn có bộ sưu tập các trình bao bọc, mỗi trình bao bọc sẽ có lệnh để lưu phần tử cụ thể đó vào kho lưu trữ của bạn và sau đó chỉ cần chạy tập hợp các lệnh để lưu trữ dữ liệu vào kho lưu trữ của bạn. Một lần nữa, vui lòng yêu cầu theo dõi. – ARM

0

Tôi nghĩ rằng một trong những lựa chọn tốt nhất là để tạo ra một chất kết dính mô hình tùy chỉnh, mà làm cho nó có thể có logic tùy chỉnh đằng sau hậu trường và mã rất tùy chỉnh phía sau.

Có lẽ những bài viết này có thể giúp bạn:

http://www.gregshackles.com/2010/03/templated-helpers-and-custom-model-binders-in-asp-net-mvc-2/

http://www.singingeels.com/Articles/Model_Binders_in_ASPNET_MVC.aspx

Cụ thể hơn tôi có lẽ sẽ mất như là đối số điều khiển một lớp tùy chỉnh với tất cả các "cơ sở" thuộc tính bao gồm. Ví dụ, lớp có thể bao gồm một từ điển liên kết tên của mỗi trường với một đối tượng hoặc một giao diện mà bạn triển khai một lần cho từng loại dữ liệu, giúp việc xử lý dữ liệu trở nên đơn giản sau này.

/Victor

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