1

I have a view model for exams. Each exam has an arbitrary number of questions. It could be 1 question or it could be 50 questions. After this gets submitted i need to loop thru the questions and check the answers. I have a solution but i feel like it's not a best practice.

int questionNumber = 1;

        while (Request.Form["Question" + questionNumber.ToString()] != null)
        {
            int questionID = Convert.ToInt32(Request.Form["Question" + questionNumber.ToString()]);
            int answerID = Convert.ToInt32(Request.Form["Answer" + questionNumber.ToString()]);

            //TODO: Check if answer is correct
        }

Unsure of another way to do this like

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult GradeTest(int? testID, string[] questionIDs, string[] answerIDs)

What i'm doing feels a little hacky. Please help OR let me know i'm on the right track. Thanks!

kcabrams
  • 97
  • 2
  • 8
  • 4
    Dont do this. Do it the right way, use a ViewModel. You're taking some of the core aspects of ASP.NET MVC and throwing them out the window. It may help you to walk through some tutorials – Jonesopolis Jan 05 '15 at 20:43
  • Yeah Jonesy it totally hurt me to do it this way. – kcabrams Jan 05 '15 at 21:13

4 Answers4

3

I really don't get the whole context but if this is submitted from a view in a form, then the form is probably built using @Html.TextBoxFor or something like that. Just take the same model as input to the post Action. Please note that any property which is not in a form field will not be included, use HiddenFor if you must have something. I've put together an example below.

YourViewModel.cs

public class YourViewModel {
    public int ExamID { get; set; }
    public string Name { get; set; }
    public List<int> QuestionIDs { get; set; }
    public List<int> AnswerIDs { get; set; }
}

YourView.cshtml

@model YourViewModel.cs
using(Html.BeginForm("PostExam", "YourController", FormMethod.Post)        
{
    @Html.HiddenFor(m => m.ExamID)
    @Html.AntiForgeryToken()
    <strong>Please enter your name</strong>
    @Html.TextBoxFor(m => m.Name)
    @*Your question and answers goes here*@
    <input type="submit" value="Hand in" />
}

YourController.cs

public class YourController : Controller
{
    [HttpPost]
    [ValidateAntiForgeryToken()]
    public ActionResult PostExam(YourViewModel Submitted)
    {
        //Handle submitted data here
        foreach(var QuestionID in Submitted.QuestionIDs)
        {
            //Do something
        }
    }
}
  • He needs to submit a collection back to the server which is a bit advanced for a beginner if we're talking about a list of complex objects rather than primitives. – The Muffin Man Jan 05 '15 at 21:05
1

I'm a ding dong. I was going about this all wrong. I looked up how to pass a collection from view to controller and problem solved!

http://www.c-sharpcorner.com/UploadFile/pmfawas/Asp-Net-mvc-how-to-post-a-collection/

I updated my view / controller like so:

@foreach (var question in Model.TestQuestions)
{
    @Html.Hidden("Questions[" + questionIndex.ToString() + "].ID", question.ID)
    <h3>@question.Text</h3>
    <section>
        @foreach (var answer in question.TestAnswers)
        {
            <div>
                @Html.RadioButton("Answers[" + questionIndex.ToString() + "].ID", answer.ID) @answer.Text
            </div>
        }
    </section>
    <hr />        
    questionIndex++;    
}

and controller:

[HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult TestDoGrade(int? testID, IEnumerable<TestQuestion> questions, IEnumerable<TestAnswer> answers)
    {
kcabrams
  • 97
  • 2
  • 8
  • 1
    that's good. One improvement you could make is to create a `TestViewModel` that contains it's ID, questions, and answers. Then you have everything encapsulated in one object. – Jonesopolis Jan 06 '15 at 01:31
0

Use a viewmodel with a list. The only caveat to this is binding to a List is somewhat of an advanced technique: Model Binding to a List MVC 4

public class Response {
   public string QuestionId {get;set;}
   public string AnswerId {get;set;}
}

public class ExamViewModel {
    public int? TestId {get;set;}
    public List<Response> Responses {get;set;}
}

public ActionResult GradeTest(ExamViewModel viewModel)
{
...
Community
  • 1
  • 1
AaronLS
  • 37,329
  • 20
  • 143
  • 202
0

You can accept JObject as parameter. JObject it will make covert form data into List of JProperties that you can enumerate.

JProperty has two fields, Name and Value.

Ali Sakhi
  • 195
  • 1
  • 1
  • 9