简体   繁体   中英

ASP.NET MVC Best way to handle dynamic number of parameters in controller

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!

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
        }
    }
}

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)
{
...

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)
    {

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM