Подтвердить что ты не робот

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

4b9b3361

Ответ 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
}

Любые мысли об этом подходе?