私はこの問題を実験していて、ある種の解決策を思いついた。InterfaceModelBinderというクラスを作成しました。
public class InterfaceModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
ModelBindingContext context = new ModelBindingContext(bindingContext);
var item = Activator.CreateInstance(
Type.GetType(controllerContext.RequestContext.HttpContext.Request.Form["AssemblyQualifiedName"]));
Func<object> modelAccessor = () => item;
context.ModelMetadata = new ModelMetadata(new DataAnnotationsModelMetadataProvider(),
bindingContext.ModelMetadata.ContainerType, modelAccessor, item.GetType(), bindingContext.ModelName);
return base.BindModel(controllerContext, context);
}
}
Application_Startに次のように登録しました:
ModelBinders.Binders.Add(typeof(IFormSubmission), new InterfaceModelBinder.Models.InterfaceModelBinder());
インターフェイスと具体的な実装は次のようになります。
public interface IFormSubmission
{
}
public class ContactForm : IFormSubmission
{
public string Name
{
get;
set;
}
public string Email
{
get;
set;
}
public string Comments
{
get;
set;
}
}
このアプローチ全体の唯一の欠点は(すでに収集しているかもしれませんが)、どこかからAssemblyQualifiedNameを取得する必要があることです。この例では、次のように、クライアント側で非表示フィールドとして格納されています。
<%=Html.HiddenFor(m => m.GetType().AssemblyQualifiedName) %>
タイプ名をクライアントに公開することの欠点が、このアプローチの利点を失う価値があるかどうかはわかりません。このようなアクションは、すべてのフォーム送信を処理できます。
[HttpPost]
public ActionResult Process(IFormSubmission form)
{
if (ModelState.IsValid)
{
FormManager manager = new FormManager();
manager.Process(form);
}
//do whatever you want
}
このアプローチについて何か考えはありますか?