Я экспериментировал с этой проблемой и придумал какое-то решение. Я сделал класс с именем 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, и в этом примере он хранится как скрытое поле на стороне клиента, например: 1010 *
<%=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
}
Есть какие-нибудь мысли об этом подходе?