MVC中复制的嵌套转发器行为

时间:2010-12-03 19:33:44

标签: asp.net-mvc foreach nested-repeater

今天我决定给MVC一个机会,虽然我非常喜欢这个想法,但我发现从ASP.NET过渡并掌握一些基本概念相当困难,比如使用foreach而不是嵌套的中继器。

我花了好几个小时来提出这个解决方案,但这似乎并不合适。有人可以解释这个代码有什么问题,以及正确的方法是什么。这是我的解决方案:

基本上,这是一项由几个问题组成的调查,每个问题都有几个答案。我在db中有表,它们表示为强类型实体。控制器如下所示:

public ActionResult Details(int id)
{
    return View(new Models.Entities().Questions.Where(r => r.PROMId == id));
}

和相应的观点如下:

<% foreach (var question in Model) { %>

    <h3>Question <%: Array.IndexOf(Model.ToArray(), question) + 1 %></h3>
    <p><%: question.QuestionPart1 %></p>
    <p><%: question.QuestionPart2 %></p>
    <% var answers = new Surveys_MVC.Models.Entities().Answers.Where(r => r.QuestionId == question.QuestionId); %>
    <% foreach (var answer in answers) { %>
        <input type="radio" /><%: answer.Text %>
    <% } %>

<% } %>

所有反馈都表示赞赏。

2 个答案:

答案 0 :(得分:2)

对于嵌套转发器行为使用for循环,我认为这是在MVC中执行此操作的最佳方法。但我建议你使用专用的ViewModels。

视图模型:

public class RadioQuestionListViewModel
{
    public IEnumerable<RadioQuestionViewModel> Questions {get;set;}
}

public class RadioQuestionViewModel
{
    public int QuestionNumber {get;set;}
    public string InputName {get;set;}
    public string QuestionPart1 {get;set;}
    public string QuestionPart2 {get;set;}
    public IEnumerable<RadioAnswerViewModel> PossibleAnswers {get;set;}
}
public class RadioAnswerViewModel
{
    public int AnswerId {get;set;}
    public string Text {get;set;}
}

控制器:

public ActionResult Details(int id)
{
    var model = GetRadioQuestionListModelById(id);
    return View(model);
}

查看:

<% foreach (var question in Model) { %>

    <h3>Question <%: question.QuestionNumber %></h3>
    <p><%: question.QuestionPart1 %></p>
    <p><%: question.QuestionPart2 %></p>
    <% foreach (var answer in question.PossibleAnswers) { %>
        <%: Html.RadioButton(question.InputName, answer.AnswerId) %>
        <%: answer.Text %>
    <% } %>
<% } %>

这种方法有一些优点:

  1. 它会阻止您的视图代码依赖于您的数据访问类。视图代码应该只负责决定如何将所需的视图模型呈现为HTML。
  2. 它将非显示相关的逻辑保留在您的视图代码之外。如果您以后决定分页问题,​​并且现在显示问题11-20而不是1-whatever,则可以使用完全相同的视图,因为控制器负责确定要显示的问题编号。
  3. 这样可以更容易避免在Array.IndexOf(Model.ToArray(), question)循环内执行for和数据库往返,如果您在页面上有多个问题,这可能会变得非常昂贵。
  4. 当然,您的单选按钮需要具有与其关联的输入名称和值,否则在提交表单时您将无法检索此信息。通过让控制器决定输入名称的生成方式,您可以更清楚地了解Details方法与SaveAnswers方法的对应关系。

    以下是GetRadioQuestionListModelById的可能实现:

    public RadioQuestionListViewModel GetRadioQuestionListModelById(int id)
    {
        // Make sure my context gets disposed as soon as I'm done with it.
        using(var context = new Models.Entities())
        {
            // Pull all the questions and answers out in a single round-trip
            var questions = context.Questions
                .Where(r => r.PROMId == id)
                .Select(r => new RadioQuestionViewModel
                    {
                        QuestionPart1 = r.q.QuestionPart1,
                        QuestionPart2 = r.q.QuestionPart2,
                        PossibleAnswers = r.a.Select(
                            a => new RadioAnswerViewModel
                                 {
                                    AnswerId = a.AnswerId,
                                    Text = a.Text
                                 })
                    })
                .ToList();
        }
        // Populate question number and name
        for(int i = 0; i < questions.Count; i++)
        {
            var q = questions[i];
            q.QuestionNumber = i;
            q.InputName = "Question_" + i;
        }
        return new RadioQuestionListViewModel{Questions = questions};
    }
    

答案 1 :(得分:0)

我不知道它是否更好,但您可以创建一个帮助程序来为您执行此操作:

public static void Repeater<T>(this HtmlHelper html, IEnumerable<T> items, string cssClass, string altCssClass, string cssLast, Action<T, string> render)
        {
            if (items == null)
                return;
            var i = 0;
            foreach (var item in items)
            {
                i++;
                if (i == items.Count())
                    render(item, cssLast);
                else
                    render(item, (i % 2 == 0) ? cssClass : altCssClass);
            }
        }

然后你可以这样称呼它:

<%Html.Repeater(Model, "css", "altCss", "lastCss", (question, css) => { %>
    <h3>Question <%: Array.IndexOf(Model.ToArray(), question) + 1 %></h3>
    <p><%: question.QuestionPart1 %></p>
    <p><%: question.QuestionPart2 %></p>
    <% var answers = new Surveys_MVC.Models.Entities().Answers.Where(r => r.QuestionId == question.QuestionId); %>
    <% foreach (var answer in answers) { %>
        <input type="radio" /><%: answer.Text %>
    <% } %>

<% }); %>

这有很多权力,上面只是一个例子。您可以在此处阅读更多内容http://haacked.com/archive/2008/05/03/code-based-repeater-for-asp.net-mvc.aspx