ASP.NET MVC - настраиваемый тип привязки по типу интерфейса
Я не уверен, ожидается ли это поведение или нет, но похоже, что привязка к пользовательской модели не работает, когда привязка привязана к типу интерфейса. Кто-нибудь экспериментировал с этим?
public interface ISomeModel {}
public class SomeModel : ISomeModel {}
public class MvcApplication : HttpApplication {
protected void Application_Start(object sender, EventArgs e) {
ModelBinders.Binders[typeof(ISomeModel)] = new MyCustomModelBinder();
}
}
С приведенным выше кодом, когда я привязываюсь к модели типа SomeModel, MyCustomModelBinder никогда не попадает; однако, если я изменю приведенный выше код и заменим typeof(ISomeModel)
на typeof(SomeModel)
и опубликую ту же самую форму, MyCustomModelBinder вызывается как ожидалось. Правильно ли это?
Edit
Я снова оказался в этом затруднительном положении через год после того, как я изначально задал этот вопрос, и теперь у меня есть решение, которое работает. Спасибо, Мэтт Хайдинг!
http://www.matthidinger.com/archive/2011/08/16/An-inheritance-aware-ModelBinderProvider-in-MVC-3.aspx
Ответы
Ответ 1
Я не уверен, что это связано напрямую, но да, есть вещи, о которых вам нужно подумать при использовании привязки к модели и интерфейсов... Я столкнулся с аналогичными проблемами с привязкой к модели по умолчанию, но это может быть не напрямую связано в зависимости от того, как вы делаете...
Посмотрите на следующее:
ASP.net MVC v2 - Отладка проблем с привязкой модели - ошибка?
ASP.net MVC v2 - Отладка проблем с привязкой к модели - BUG?
Ответ 2
Я экспериментировал с этой проблемой, и я придумал решение. Я создал класс под названием 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
}
Любые мысли об этом подходе?
Ответ 3
Внезапно появляется решение MVC3:
http://www.matthidinger.com/archive/2011/08/16/An-inheritance-aware-ModelBinderProvider-in-MVC-3.aspx