ASP.NET MVC多选项问卷开发:视图实现与数据存储方案咨询
Hey there! Let's break down how to fix your questionnaire view and handle saving responses correctly in ASP.NET MVC. First, let's diagnose the core issue with your current code: when using foreach with RadioButtonFor, the generated HTML creates radio buttons that don't group properly per question, and MVC's model binding won't be able to map each answer back to its corresponding QuestionID reliably. Here's the step-by-end solution:
1. Create a ViewModel for Responses
First, define a ViewModel to separate what's displayed/collected in the view from your database entities. This makes model binding cleaner and more maintainable:
public class QuestionResponseViewModel { public int QuestionID { get; set; } public string QuestionText { get; set; } [Required(ErrorMessage = "Please select an answer for this question.")] public string SelectedAnswer { get; set; } } // Main view model to hold all questions public class QuestionnaireViewModel { public List<QuestionResponseViewModel> Questions { get; set; } }
2. Fix the View (Two Solid Options)
Option 1: Use Editor Templates (Clean & Scalable)
Editor Templates are the best practice for rendering repeated items and ensuring proper model binding.
Step 2.1: Create the Editor Template
Create a folder Views/Shared/EditorTemplates (if it doesn't exist), then add a partial view named QuestionResponseViewModel.cshtml:
@model QuestionResponseViewModel <tr> <td>@Model.QuestionText</td> <td class="text-center"> @Html.RadioButtonFor(m => m.SelectedAnswer, "Strongly Agree", new { id = $"SA_{Model.QuestionID}" }) </td> <td class="text-center"> @Html.RadioButtonFor(m => m.SelectedAnswer, "Somewhat Agree", new { id = $"WA_{Model.QuestionID}" }) </td> <td class="text-center"> @Html.RadioButtonFor(m => m.SelectedAnswer, "Strongly Disagree", new { id = $"SD_{Model.QuestionID}" }) </td> </tr>
Step 2.2: Update the Main View
Now your main view can use EditorFor to render all questions with proper grouping:
@model QuestionnaireViewModel @using (Html.BeginForm("SubmitResponses", "Questionnaire", FormMethod.Post)) { <table class="table"> <tr> <th>Question</th> <th class="text-center">Strongly Agree</th> <th class="text-center">Somewhat Agree</th> <th class="text-center">Strongly Disagree</th> </tr> @Html.EditorFor(m => m.Questions) </table> <button type="submit" class="btn btn-primary">Submit Responses</button> @Html.ValidationSummary(true) }
Option 2: Use a for Loop (Straightforward)
If you prefer avoiding Editor Templates, use a for loop (requires Questions to be an IList):
@model QuestionnaireViewModel @using (Html.BeginForm("SubmitResponses", "Questionnaire", FormMethod.Post)) { <table class="table"> <tr> <th>Question</th> <th class="text-center">Strongly Agree</th> <th class="text-center">Somewhat Agree</th> <th class="text-center">Strongly Disagree</th> </tr> @for (int i = 0; i < Model.Questions.Count; i++) { <tr> <td>@Model.Questions[i].QuestionText</td> <td class="text-center"> @Html.RadioButtonFor(m => m.Questions[i].SelectedAnswer, "Strongly Agree") </td> <td class="text-center"> @Html.RadioButtonFor(m => m.Questions[i].SelectedAnswer, "Somewhat Agree") </td> <td class="text-center"> @Html.RadioButtonFor(m => m.Questions[i].SelectedAnswer, "Strongly Disagree") </td> @Html.HiddenFor(m => m.Questions[i].QuestionID) </tr> } </table> <button type="submit" class="btn btn-primary">Submit Responses</button> @Html.ValidationSummary(true) }
3. Controller Logic to Load & Save Responses
Step 3.1: Load Questions into the View
In your controller, fetch questions from tblQuestions and map them to your ViewModel:
public class QuestionnaireController : Controller { private readonly YourDbContext _dbContext; // Inject your DbContext public QuestionnaireController(YourDbContext dbContext) { _dbContext = dbContext; } public IActionResult Index() { var questions = _dbContext.tblQuestions .Select(q => new QuestionResponseViewModel { QuestionID = q.QuestionID, QuestionText = q.Question }) .ToList(); var viewModel = new QuestionnaireViewModel { Questions = questions }; return View(viewModel); } }
Step 3.2: Handle Submission & Save to Database
Add a POST action to validate responses and save them to tblTestProper:
[HttpPost] [ValidateAntiForgeryToken] public IActionResult SubmitResponses(QuestionnaireViewModel viewModel) { if (!ModelState.IsValid) { return View("Index", viewModel); } // Get current user's ID (adjust based on your auth system) int currentUserId = int.Parse(User.FindFirst(ClaimTypes.NameIdentifier).Value); foreach (var response in viewModel.Questions) { var userResponse = new tblTestProper { QuestionID = response.QuestionID, UserID = currentUserId, Answer = response.SelectedAnswer }; _dbContext.tblTestProper.Add(userResponse); } _dbContext.SaveChanges(); return RedirectToAction("ThankYou"); // Redirect to a confirmation page } public IActionResult ThankYou() { return View(); }
Key Takeaways
- Model Binding:
foreachfails here because it doesn't track item indexes, making it impossible for MVC to map answers to their questions. Editor Templates orforloops fix this by preserving index information. - Validation: The
[Required]attribute ensures users can't submit incomplete responses. - Database: Adjust the DbContext code to match your actual entity classes. If you're not using Entity Framework, use ADO.NET to insert records directly into
tblTestProper.
内容的提问来源于stack exchange,提问作者mj.e

