Как я могу вызвать связыватель пользовательской модели в MVC4?

Таким образом, похоже, что несколько человек (например, здесь и здесь) были проблемы с привязкой модели MVC4 для ApiControllers, но ни один из них не решает проблемы проблема, которую я вижу.

Все, что я действительно хотел бы сделать, это изменить поведение привязки массива для списков целых чисел. Итак, скажем, у меня был такой тип запроса:

public class MyRequestModel
{
    public List<long> ListOfIntegers { get; set; }

    ...
}

И такой метод API GET:

public ResultsResponseModel Get(MyRequestModel request)
{
    // use request.ListOfIntegers meaningfully

    ...

    return response;
}

Я в основном хочу иметь возможность сказать /api/results/?listOfIntegers=1+2+3+4+5 и иметь это разрешение для свойства List<long>.

Я попробовал свои обычные трюки с привязкой модели, но, как и в большинстве веб-API в MVC4, он имеет совершенно отдельный путь для привязки модели.

Самое большее, что я получил, это использование атрибута System.Web.Http.ModelBinding.ModelBinder для MyRequestModel и создание связывателя модели, который «реализовал» System.Web.Http.ModelBinding.IModelBinder. Это постоянно приводит к исключению ссылки на объект с трассировкой стека, которая никогда не затрагивает мой код.

Кто-нибудь попал в это? Есть мысли, что попробовать дальше?

ОБНОВЛЕНИЕ: вот трассировка стека, которую я зафиксировал в своем пользовательском ExceptionFilterAttribute:

Object reference not set to an instance of an object.
    at System.Web.Http.ModelBinding.DefaultActionValueBinder.BindParameterValue(HttpActionContext actionContext, HttpParameterBinding parameterBinding)
    at System.Web.Http.ModelBinding.DefaultActionValueBinder.<>c__DisplayClass1.BindValuesAsync>b__0(RequestContentReadKind contentReadKind)
    at System.Threading.Tasks.TaskHelpersExtensions.<>c__DisplayClass38.<ToAsyncVoidTask>b__37()
    at System.Threading.Tasks.TaskHelpers.RunSynchronously[TResult](Func`1 func, CancellationToken cancellationToken)

person Brandon Linton    schedule 10.04.2012    source источник
comment
Вы можете отправить трассировку стека?   -  person jorgehmv    schedule 11.04.2012


Ответы (1)


Если вы говорите об ApiControllers, то вы пытаетесь привязать модель в веб-API, а теперь и в MVC. Вот пример привязки модели.

  public class MyRequestModelBinderProvider : ModelBinderProvider
    {
        MyRequestModelBinder binder = new MyRequestModelBinder();
        public IdeaModelBinderProvider()
        {          
        }

        public override IModelBinder GetBinder(HttpActionContext actionContext, ModelBindingContext bindingContext)
        {
            if (bindingContext.ModelType == typeof(MyRequestModel))
            {
                return binder;
            }

            return null;
        }
    } 

Вот пример регистрации поставщика связывателя пользовательской модели.

 IEnumerable<object> modelBinderProviderServices = GlobalConfiguration.Configuration.ServiceResolver.GetServices(typeof(ModelBinderProvider));
 List<Object> services = new List<object>(modelBinderProviderServices);
 services.Add(new MyRequestModelBinderProvider());
 GlobalConfiguration.Configuration.ServiceResolver.SetServices(typeof(ModelBinderProvider), services.ToArray());

Теперь в вашем пользовательском связывателе модели вы используете контексты для доступа к значениям строки запроса.

  public class MyRequestModelBinder :  IModelBinder
    {
        public MyRequestModelBinder()
        {

        }

        public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
        {
            MyRequestModel yourModel; 
            //use contexts to access query string values
            //create / update your model properties

            bindingContext.Model = yourModel;  
            //return true || false if binding is successful
        }

Убедитесь, что вы используете классы и интерфейсы для WebAPI, а не MVC. Некоторые имена совпадают, но разные пространства имен и DLL

person cecilphillip    schedule 11.04.2012
comment
Итак, я думаю, что у меня есть правильная структура. Знаете ли вы, есть ли способ вызвать привязку модели веб-API по умолчанию, а затем просто вручную управлять параметрами значений списка в связывателе модели? - person Brandon Linton; 11.04.2012
comment
вызовите base.GetBinder в своем пользовательском ModelBinderProvide, чтобы получить связыватель модели по умолчанию, и поместите его в частное поле вашего пользовательского связывателя модели, чтобы вы могли использовать его из своего связывателя модели. - person Francesco Abbruzzese; 12.04.2012
comment
@FrancescoAbbruzzese Я бы хотел сделать что-то подобное - к сожалению, поскольку мы наследуем от ModelBinderProvider, который является абстрактным классом с абстрактным базовым методом GetBinder, для меня нет готового метода по умолчанию. Я пробовал отправлять экземпляры из типов в System.Web.Http.Modelbinding.ModelBinders без особого успеха. Любые другие идеи? - person Brandon Linton; 12.04.2012
comment
ммм, дело в том, что биндер модели по умолчанию не является общедоступным, и поскольку мы не можем получить копию биндера модели по умолчанию... трудно найти выход. Кажется, что есть выбор дизайна, чтобы полностью скрыть функциональные возможности связывателя модели по умолчанию. Возможный подвох: использовать исходники, которые сейчас доступны, для определения связывателя модели PUBLIC по умолчанию... Излишне говорить, что мне это не нравится :) В ближайшие дни я начну изучать исходники... так что может быть, я найду какую-то другую точку расширения ... но я чувствую, что привязка модели по умолчанию была полностью скрыта - person Francesco Abbruzzese; 12.04.2012
comment
+1 и принято - я все еще хотел бы иметь возможность сначала вызывать привязку модели по умолчанию в пользовательском связующем, что, вероятно, должно стать отдельным вопросом. Это дало мне точку расширения, которую я искал, чтобы взять под контроль привязку модели для определенного типа. @FrancescoAbbruzzese У меня, вероятно, скоро будет еще один вопрос по этому поводу, если вы найдете какие-либо другие крючки :) - person Brandon Linton; 12.04.2012
comment
Вы должны иметь возможность вызывать GetServices для объекта GlobalConfiguration.Configuration.ServiceResolver, чтобы получить значение по умолчанию. Тогда, возможно, вы можете обернуть его своим обычаями и вызывать при необходимости... хотя это всего лишь предположение. - person cecilphillip; 13.04.2012
comment
@BrandonLinton, может быть, я вас неправильно понял, но если вы вернете нулевое значение в своем пользовательском провайдере, система автоматически вернется к значениям по умолчанию. Проверьте это сообщение в блоге bradwilson.typepad.com/ блог/2010/10/ - person Calin; 18.04.2012
comment
@Calin Да, этот пост относится к MVC3, хотя он выглядит так же, как с ModelBinderProviders. Загвоздка здесь в том, что я по-прежнему хочу, чтобы провайдер возвращал мою пользовательскую привязку модели — я просто хочу, чтобы внутри этого провайдера сначала «запустилась» привязка модели по умолчанию, а затем поверх нее была наложена моя собственная пользовательская логика привязки списка. - person Brandon Linton; 18.04.2012
comment
@BrandonLinton, код mvc находится в кодеплексе, возьмите этот код связующего и вставьте в свой проект, класс не является общедоступным :) - person Calin; 18.04.2012
comment
Есть новости по этому поводу? Похоже, что это не работает для MVC 4 RC, поскольку DependencyResolver отличается. - person Brian Vallelunga; 21.06.2012
comment
Похоже, они все еще вносят изменения в общедоступный API. Я бы дождался RTM, чтобы увидеть, что в итоге получится - person cecilphillip; 21.06.2012
comment
Почему функция BindModel() возвращает логическое значение? Если бы я мог успешно извлечь нужный контент и десериализовать его в объект, как я должен передать объект на следующую фазу? - person Believe2014; 06.03.2015
comment
В случае успеха вы устанавливаете созданную модель в контексте привязки. я обновил пост - person cecilphillip; 08.03.2015