2014-01-13 78 views
1

我想用一个视图来做到这一点,也许这不是最好的方式?如何从不同的ViewModel返回值?

我有一个数据驱动的表单;问题都来自一个问题表,但答案填充一个答复表。这里是视图;

@using Microsoft.AspNet.Identity 
    @model Template.Models.Question 

@{ 
    ViewBag.Title = "View question"; 
    var qtype = Model.QuestionTypeId; 
    ViewBag.Number = Model.Id - 7; 
    Html.BeginForm("ViewQuestion", "Question", FormMethod.Post, new { @class = "form-horizontal", role = "form" }); 
} 

<h4>Question #@ViewBag.Number</h4> 
<hr /> 
<h1> @Model.Question1</h1> 

@Html.AntiForgeryToken() 
<div> 

    @switch (qtype) 
    { 
     case 1: 
      // Textbox 
      @Html.TextArea("Answer", new { @class = "form-control", rows = "4", col = "5" });<br /><br /> 
      break; 
     case 2: 
      // Dropdown 
      <select class="form-control" id="Answer"> 
      @foreach (var item in Model.QuestionOptions.OrderBy(o => o.QuestionOptionRanking)) 
      { 
       <option value="@item.QuestionOption1">@item.QuestionOption1</option> 

      } 
      </select><br /><br /> 
      break; 
     case 3: 
      // Checkbox 
       <div class="checkbox"> 
       @foreach (var item in Model.QuestionOptions.OrderBy(o => o.QuestionOptionRanking)) 
        { 
        <input type="checkbox" name="Answer" value="@item.QuestionOption1" /> @item.QuestionOption1 <br /> 
        } 
       </div><br /><br /> 
       break; 
     case 4: 
      //  Radio buttons 
      foreach (var item in Model.QuestionOptions.OrderBy(o => o.QuestionOptionRanking)) 
      { 
       <div class="radio"> 
       <label> 
       <input type="radio" name="Answer" value="@item.QuestionOption1" /> 
       @item.QuestionOption1 
       </label> 
       </div> 
      }<br /><br /> 
      break; 
    } 

</div> 
<input type="hidden" name="QuestionId" value="@Model.Id" /> 
<input type="hidden" name="UserId" value="@User.Identity.GetUserId()" /> 
<div class="form-group"> 
    <div class="col-md-offset-2 col-md-10"> 
     <input type="submit" class="btn btn-default" value="Answer" /> 
    </div> 
</div> 

这里是我的视图模型;

using System; 
using System.Collections.Generic; 
using System.ComponentModel.DataAnnotations; 

namespace Template.Models 
{ 
public class GetQuestionViewModel 
{ 

    public int Id { get; set; } 

    public int PageNumber { get; set; } 

    public string Question1 { get; set; } 

    public int QuestionTypeId { get; set; } 

    public Nullable<int> LinkedTo { get; set; } 

    public Nullable<int> Options { get; set; } 

    public Nullable<int> QuestionRanking { get; set; } 

    public virtual ICollection<QuestionOption> QuestionOptions { get; set; } 

    public virtual QuestionType QuestionType { get; set; } 

    public virtual ICollection<Response> Responses { get; set; } 

    public virtual ICollection<AspNetUser> AspNetUsers { get; set; } 
} 

public class ResponseViewModel 
{ 
    [Required] 
    public string UserId { get; set; } 

    [Required] 
    public int QuestionId { get; set; } 

    [Required(ErrorMessage = "Please answer the question before submitting")] 
    public string Answer { get; set; } 

    [Required] 
    public string Source { get; set; } 

    [Required] 
    public string Status { get; set; } 

    [Required] 
    public System.DateTime DateStamp { get; set; } 

    public Nullable<int> Duplicate { get; set; } 

    public virtual Question Questions { get; set; } 

    public object SelectedValue { get; set; } 

    public virtual ICollection<QuestionOption> QuestionOptions { get; set; } 
} 
} 

我不能让GetQuestionViewModel在所有的工作,并使出了问题型号的.emdx版本来填充页面,但我的任务是:a)显示每个用户在网页上的答案,和b)在PageNumber字段中显示问题,因为可以在单个屏幕上显示任意数量的问题。

这是我的控制器,但你可以看到更多的是被评论的比工作!

using System.Web.Mvc; 
using Template.Models; 

namespace Template.Controllers 
{ 
public class QuestionController : Controller 
{ 
    private WebTemplateEntities db = new WebTemplateEntities(); 

    // GET: /Questions/ViewQuestion/5 
    public ActionResult ViewQuestion(int? id) 
    { 
     if (id == null || id == 0 || id > 12) 
     { 
      id = 8; 
     } 
     var question = db.Questions.Find(id); 
     if (question == null) 
     { 
      return HttpNotFound(); 
     } 

     return View(question); 
    } 

    //  [Route("Questions/{page?}")] 
    //public ActionResult ViewQuestion(GetQuestionViewModel model, int? id) 
    //{ 
    // if (id == null || id == 0 || id > 12) 
    // { 
    //  //return new HttpStatusCodeResult(HttpStatusCode.BadRequest); 
    //  id = 8; 
    //  //return RedirectToAction("/ViewQuestion/" + id); 
    // } 

    // var q = db.Questions.Find(id); 
    // var pageId = q.PageNumber; 

    // var questions = from q in db.Questions 
    //     where q.PageNumber == pageId 
    //     orderby q.QuestionRanking 
    //     select q; 
    // return View(questions); 
    // } 

    // POST: /Questions/ViewQuestion/5 
    [HttpPost] 
    public ActionResult ViewQuestion([Bind(Include = "QuestionId, Answer, UserId")] ResponseViewModel responseViewModel) 
    { 
     Response re = new Models.Response(); 
     re.Answer = responseViewModel.Answer; 
     if (re.Answer == null) 
     { 
      re.Answer = "Work in progress!"; 
      // re.Answer = responseViewModel.SelectedValue(); 
      // re.Answer = int.Parse(SelectList["Question.QuestionOption1"]); 
     } 
     re.UserId = responseViewModel.UserId; 
     re.QuestionId = responseViewModel.QuestionId; 
     var id = responseViewModel.QuestionId; 
     re.Source = "Web"; 
     re.Status = "New"; 
     re.DateStamp = System.DateTime.Now; 
     db.Responses.Add(re); 
     db.SaveChanges(); 

     return RedirectToAction("ViewQuestion/" + (id + 1)); 
    } 

    protected override void Dispose(bool disposing) 
    { 
     if (disposing) 
     { 
      db.Dispose(); 
     } 
     base.Dispose(disposing); 
    } 
    } 
} 

我一直在这个问题上工作几天,现在我只是绕着圈子走。任何帮助或建议,我非常感谢。

+0

顺便说一句,让视图中显示的问题号码基于ID是不好的做法。Id应该完全不知道你的实现。应该有一个单独的字段用于存储实际的问题编号。 –

+0

同意。我打算在能够正常工作时切换到页码。但我暂时无法显示用户的答案。两者都困扰着我! – Alex

+0

你的[Route ...]工作吗?当您导航到该网址时,控制器方法是否执行? –

回答

0

看起来你很迷惑实体模型和ViewModels。在你看来,你参考Template.Models.Question,这似乎是一个实体,而不是一个ViewModel。

该视图应只引用您的ViewModel,而不是您的实体。并且你的实体和你的ViewModel之间应该建立一些映射机制。我注意到的另一件事是您的实体和您的ViewModel似乎驻留在您的应用程序中相同的命名空间。这不一定是个问题,但可能会让人困惑,因为ViewModels仅适用于您的视图,实体适用于您的持久层。

当前您的代码通过以下方法获取问题对象:from q in db.Questions然后您将该问题传递给View(问题)。所以你不会在视图中提供你的ViewModel,它将会是你的实体模型。

您的视图更改为:

@model Template.Models.GetQuestionViewModel 

,改变你的控制器是这样的:

var questions = (from q in db.Questions 
       where q.PageNumber == pageId 
       orderby q.QuestionRanking 
       select q.ToViewModelExtensionMethod()).ToList(); 

return View(questions); 

你必须编写扩展方法来从Template.Model执行映射。问题到Template.Model.GetQuestionViewModel。

希望这会有所帮助。

更新

有关扩展方法的信息,这里的MSDN article

我只是使用扩展方法,因为我喜欢语法流动的方式。这里有一个简单的例子:

namespace Template.Models.Extensions 
{ 
    public static class QuestionExtensions 
    { 
     public static GetQuestionViewModel ToViewModelExtensionMethod(this Question question) 
     { 
      var result = new GetQuestionViewModel(); 
      result.field1 = question.field1; 
      // ... etc 
      return result; 
     } 
    } 
} 

此外,请注意上面的变化,我现在正在映射项目,因为他们被查询。

+0

你是正确的亚伦,我很困惑的实体和视图模型。你能否给我任何更多的细节或“扩展方法执行映射”的含义,因为这是使我困惑的部分? – Alex