How to model bind a class that implements an interface?

asp.net-mvc-3

Solution

what am i doing wrong?

You cannot expect the model binder to know that when it encounters the `IQuestionAnswer` interface on your `SurveyAnswer` view model it should use the `QuestionAnswer` type. It's nice that you have declared this implementation of the interface but the model binder has no clue about it.

So you will have to write a custom model binder for the `IQuestionAnswer` interface (same for the `IHiddenAnswer` interface) and indicate which implementation do you want to be used:

public class QuestionAnswerModelBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        var type = typeof(QuestionAnswer);
        var model = Activator.CreateInstance(type);
        bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, type);
        return model;
    }
}

which will be registered in your `Application_Start`:

ModelBinders.Binders.Add(typeof(IQuestionAnswer), new QuestionAnswerModelBinder());

Problem

The model binding worked fine until i implemented interfaces on top of the following classes: ``` public class QuestionAnswer : IQuestionAnswer { public Int32 Row_ID { get; set; } public Int32 Column_ID { get; set; } public String Value { get; set; } } public class HiddenAnswer : IHiddenAnswer { public Int32 Hidden_Field_ID { get; set; } public String Hidden_Field_Value { get; set; } } public class SurveyAnswer : ISurveyAnswer { public string SessionID { get; set; } public List<IQuestionAnswer> QuestionAnswerList { get; set; } public List<IHiddenAnswer> HiddenAnswerList { get; set; } public SurveyAnswer() { QuestionAnswerList = new List<IQuestionAnswer>(); HiddenAnswerList = new List<IHiddenAnswer>(); } } ``` Now that the interfaces are there, i get a `500 (Internal Server Error)` The javascript that i use to model bind is the following: ``` $('#submitbutton').click(function () { var answers = new Array(); var hiddenfields = new Array(); var formname = "#" + $("#formname").val(); $(':input', formname).each(function () { if ($(this).is(":text") || $(this).is(":radio") || $(this).is(":checkbox")) { var answerObject = { Column_ID: $(this).attr('data-column_id'), Row_ID: $(this).attr('data-row_id'), Value: $(this).attr('data-theValue') }; answers.push(answerObject); } else if($(this).is(":hidden")) { var hiddenObject = { Hidden_Field_ID: $(this).attr('data-hidden_field_id'), Hidden_Field_Value: $(this).attr('data-hidden_field_value') } hiddenfields.push(hiddenObject); } }); $('textarea', formname).each(function () { var answerObject = { Column_ID: $(this).attr('data-column_id'), Row_ID: $(this).attr('data-row_id'), Value: $(this).val(), }; answers.push(answerObject); }); var allAnswers = { SessionID: 0, QuestionAnswerList: answers, HiddenAnswerList: hiddenfields } postForm(allAnswers); }); ``` The Controller Action looks like this: ``` [AcceptVerbs(HttpVerbs.Post)] public ActionResult SubmitSurvey(SurveyAnswer answers) { // Dette tillader CORS Response.AppendHeader("Access-Control-Allow-Origin", "*"); bc.SaveSurvey(answers); return null; } ``` what am i doing wrong?

Original source