這就是我將如何開始解決這個問題。基於FormKey屬性(可以通過索引和/或標籤來確定)來構建自定義模型綁定器非常容易。
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; }
}
此外,我注意到下面的評論之一有一個過濾模型活頁夾系統。 Automapper的Jimmy Bogard在http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/03/17/a-better-model-binder.aspx上發佈了一個關於此方法的非常有用的帖子,後來在http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/11/19/a-better-model-binder-addendum.aspx中進行了修訂。這對我構建定製模型活頁夾非常有幫助。
更新
我意識到,我誤解了問題,他專門詢問如何處理表「上與代表不同數據類型的輸入字段的變量」的帖子。我認爲這樣做的最好方法是使用類似於上面的結構,但使用Composite Pattern。基本上,您需要創建一個接口,如IFormComponent
,並針對將要表示的每種數據類型實施它。我寫了,並評論的示例界面,以幫助解釋如何做到這一點來完成:
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);
}
我假設自定義窗體可以通過某種ID的可包含一個表單參數進行訪問。有了這個假設,模型綁定器和提供者可以看起來像這樣。
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.");
}
}
顯然,這裏所有的代碼只是爲了傳達出點,將需要完成,並清理了實際使用。而且,即使完成,它也只會綁定到IForm接口的實現,而不是強類型的業務對象。(將它轉換爲字典並使用Castle DictionaryAdapter構建強類型代理並不是一個巨大的步驟,但由於您的用戶正在網站上動態創建表單,因此您的解決方案中可能沒有強類型模型,這是無關緊要的)。希望這有助於更多。
感謝您的評論,非常有見地。 – DanP 2010-12-17 10:44:05