ASP.NET MVC - привязка пользовательской модели к типу интерфейса - PullRequest
18 голосов
/ 04 июня 2010

Я не уверен, ожидается ли это поведение или нет, но кажется, что привязка пользовательской модели не работает, когда привязка назначается типу интерфейса. Кто-нибудь экспериментировал с этим?

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 будет вызываться как положено. Это кажется правильным?


Редактировать

Я снова оказался в этом затруднительном положении спустя год после того, как первоначально задал этот вопрос, и теперь у меня есть решение, которое работает. Спасибо Мэтт Хидингер!

http://www.matthidinger.com/archive/2011/08/16/An-inheritance-aware-ModelBinderProvider-in-MVC-3.aspx

Ответы [ 3 ]

10 голосов
/ 04 июля 2010

Я экспериментировал с этой проблемой и придумал какое-то решение. Я сделал класс с именем 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
}

Есть какие-нибудь мысли об этом подходе?

8 голосов
/ 19 октября 2011
5 голосов
/ 04 июня 2010

Я не уверен, имеет ли это прямое отношение, но да, есть вещи, о которых вам нужно подумать при использовании привязки модели и интерфейсов ... Я столкнулся с похожими проблемами со связывателем модели по умолчанию, но он может быть не связан в зависимости от того, как вы делаете вещи ...

Посмотрите на следующее: ASP.net MVC v2 - Проблемы отладки модели - BUG? ASP.net MVC v2 - Проблемы отладки модели - BUG?

...