Как я могу создать настраиваемый связыватель моделей, который будет возвращать разные типы моделей в зависимости от контекста запроса?

У меня есть входящие запросы (от Facebook для обработки кредитов) на конкретное действие, которое будет иметь разное содержимое, поэтому у меня есть разные классы моделей для обработки этого.

Это мое действие:

public ActionResult Index([ModelBinder(typeof(FacebookCreditModelBinder))] IFacebookRequest facebookRequest)
{
    if (facebookRequest is FacebookPaymentsGetItemsRequest)
    {
        // do whatever
    }
}

А это моя модель-папка.

public class FacebookCreditModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var binder = new DefaultModelBinder();
        // how to change the model here in the bindingContext?
        return binder.BindModel(controllerContext, bindingContext); 
    }
}

Я хочу создать, например, объект FacebookPaymentsGetItemsRequest, если входящая переменная "method" - это "payments_get_items", а FacebookPaymentsStatusUpdateRequest, если метод - "payments_status_update", и я не знаю, как изменить тип модели в контексте привязки. Можно ли изменить тип модели в настраиваемой подшивке модели?


Другой подход: я также пробовал это с BindModel, и я могу вернуть правильный объект, но все свойства равны нулю, потому что он не заполняется связывателем модели по умолчанию:

public override object BindModel(ControllerContext controllerContext,
        ModelBindingContext bindingContext)
{
    NameValueCollection form = controllerContext.HttpContext.Request.Form;
    if (form.Get("method") == "payments_get_items")
    {
        return new FacebookPaymentsGetItemsRequest();
    }
    ...

person Marc    schedule 26.11.2011    source источник


Ответы (1)


Вы могли сделать это:

public class FacebookCreditModelBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        var methodValue = bindingContext.ValueProvider.GetValue("method");
        if (methodValue == null || string.IsNullOrEmpty(methodValue.AttemptedValue))
        {
            throw new Exception("The method parameter was not found");
        }

        var method = methodValue.AttemptedValue;
        IFacebookRequest model = null;
        if (method == "payments_get_items")
        {
            model = FacebookPaymentsGetItemsRequest();
        }
        else if (method == "...")
        {
            model = ....
        }
        else
        {
            throw new NotImplementedException("Unknown method value: " + method);
        }

        bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType());
        return model;
    }
}

и зарегистрируйтесь в Application_Start:

ModelBinders.Binders.Add(typeof(IFacebookRequest), new FacebookCreditModelBinder());

Тогда действие вашего контроллера может выглядеть так:

public ActionResult Index(IFacebookRequest facebookRequest)
{
    if (facebookRequest is FacebookPaymentsGetItemsRequest)
    {
        // do whatever
    }
}
person Darin Dimitrov    schedule 26.11.2011
comment
Это отлично работает, большое спасибо! Мне не хватало (и я не мог понять) строку bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType());. Как вы узнали, что нужно использовать ModelMetadataProviders.Current? Мне нужно больше узнать о привязке модели, я думаю ... знаете хороший источник? - person Marc; 27.11.2011