環境:ASP.NET MVC Codeplex Preview 5。
UpdateModel(x, Request.Form.AllKeys):表單含非模型欄位會報錯(The model of type 'MyModel' does not have a property named 'Name2')。UpdateModel(x, new[] {"IDX","Name"}):需逐一列出欄位,繁瑣。View: 表單欄位加前綴 my.
<%using (Html.Form("home","about",FormMethod.Post)){%>
<%=Html.TextBox("my.ID")%>
<%=Html.TextBox("my.Name")%>
<%=Html.SubmitButton()%>
<%}%>
Controller: 參數標註 [ModelBinder(typeof(MyModelBinder))]。
[AcceptVerbs("post")]
public ActionResult About([ModelBinder(typeof(MyModelBinder))] MyModel my) {
ViewData["Title"] = my.Name + my.ID;
return View();
}
關鍵邏輯:反射取得模型屬性,組合 前綴.屬性名 匹配表單欄位,逐一綁定並處理驗證錯誤。
public class MyModelBinder : IModelBinder {
public object GetValue(ControllerContext controllerContext, string modelName, Type modelType, ModelStateDictionary modelState) {
object model = Activator.CreateInstance(modelType);
var keys = modelType.GetProperties().Select(c => c.Name);
string objectPrefix = modelName; // "my"
var properties = TypeDescriptor.GetProperties(model);
var dictionary = new Dictionary<string, PropertyDescriptor>();
foreach (string str in keys) {
if (!string.IsNullOrEmpty(str)) {
var descriptor = properties.Find(str, true);
if (descriptor == null) throw new ArgumentException(...);
string str3 = string.IsNullOrEmpty(objectPrefix) ? str : (objectPrefix + "." + str); // "my.ID"
dictionary[str3] = descriptor;
}
}
foreach (var pair in dictionary) {
string key = pair.Key;
var descriptor2 = pair.Value;
object obj2 = ModelBinders.GetBinder(descriptor2.PropertyType).GetValue(controllerContext, key, descriptor2.PropertyType, modelState);
if (obj2 != null) {
try { descriptor2.SetValue(model, obj2); continue; }
catch {
modelState.AddModelError(key, Convert.ToString(obj2), "驗證失敗...");
}
}
}
return model;
}
}
在 Application_Start 註冊:ModelBinders.Binders.Add(typeof(MyModel), new MyModelBinder());,Action 參數即可直接寫 MyModel my 無需屬性標註。