在关注点之间传递变量的正确方法

时间:2013-01-03 17:24:41

标签: c# asp.net-mvc razor

我希望这是一个正确的问题。如果没有,请告知适当的位置。

意图:用户可以从下拉列表中选择年份和月份。提交表单时,将生成一个图表,以根据选择显示信息。

实施:我的控制器为每个下拉列表创建IEnumerable<SelectListItem>,并存储在模型中,并传递给视图。在视图中是对图表操作的调用。

代码

控制器:

public ActionResult Validate(int month = 0, int year = 0)
    {
        //Check if input is valid
        if (month != 0 && year != 0)
        {
            ViewBag.Valid = 1;
        }
        else
        {
            ViewBag.Valid = 0;
        }
        ValidateVM model = new ValidateVM();

                    //Populate years
        model.year = Enumerable.Range(2008, DateTime.Now.Year - 2007).Reverse()
                        .Select(r => new SelectListItem
                        {
                            Value = r.ToString(),
                            Text = r.ToString()
                        });
                    //Populate months
        string[] months = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };

        model.month = months
                        .Select((r, index) => new SelectListItem { Text = r, Value = (index + 1).ToString() });

        return View(model);
    }

型号:

public class ValidateVM
{
    public int[] Mdays { get; set; }
    public IEnumerable<SelectListItem> month { get; set; }
    public IEnumerable<SelectListItem> year { get; set; }
}

查看:

@using (Html.BeginForm("Mday", "Metrics", FormMethod.Get))
{
    @Html.DropDownListFor(model => model.month, Model.month)
    @Html.DropDownListFor(model => model.year, Model.year)

    <input type="submit" />
}
@if (ViewBag.Valid == 1)
{
    <img src="@Url.Action("ValidateChart", new { month = Request.QueryString["month"],
                                        year = Request.QueryString["year"]
  })" alt="Test"/>
}

问题: 提交表单后,一切正常。我见过模型将包含所选项的变量的方法,例如int chosenMonth。以任何方式做事都有好处吗?

以我的方式使用viewbag是否可以?我用它来告诉视图输入是否有效,这决定了是否显示图表。我多次听说过不要使用viewbag。为什么是这样?模仿我描述的行为的最佳做法是什么?

在我对ValidateChart(视野内)功能的调用中,我需要利用querystring告诉操作生成图表的月份和年份。我也看到过这种做法,但没有找到更好的方法。在不经过querystring范围的情况下访问request变量的正确方法是什么?

我已经设法让这段代码进入工作状态,但我真的想学习如何以正确的方式做到这一点。

3 个答案:

答案 0 :(得分:3)

首先,如果它有效,那肯定不是错误的方式。它可能不是最好的方式,但并没有错。

以下是我要改变的一些事情,你可以决定是否要使用它们......

1)我不会使用ViewBag,只需向ViewModel添加其他属性,例如bool CanShowChart。然后,您可以在View逻辑中简单地测试它

2)为string SelectedMonthstring SelectedYear添加更多属性,这些属性可以根据参数在控制器中分配。您不必担心第一次加载时出错,因为只有在CanShowChart为真时才会使用它们。然后,您可以在视图中检查这两个值,而不是使用Request.QueryString方法

3)我更愿意将可用月份和年份列表传递给我的ViewModel,并让ViewModel按需创建SelectList。

为了清楚起见,我的ViewModel将是这样的:

public class ValidateViewModel
{
    public int[] Mdays { get; set; }
    public IEnumerable<string> AvailableMonths { get; set; }
    public IEnumerable<string> AvailableYears { get; set; }
    public string SelectedMonth { get; set; }
    public string SelectedYear { get; set; }
    public bool CanShowChart { get; set; }

    public List<SelectListItem> GetMonthsSelectList()
    {
        List<SelectListItem> list = new List<SelectListItem>();

        foreach(var month in AvailableMonths)
        {
            bool selected = month == SelectedMonth;
            list.Add(new SelectListItem() { Text = month, Value = month, Selected = selected });
        }

        return list;
    }

    public List<SelectListItem> GetYearsSelectList()
    {
        List<SelectListItem> list = new List<SelectListItem>();

        foreach(var year in AvailableYears)
        {
            bool selected = month == SelectedYear;
            list.Add(new SelectListItem() { Text = year, Value = year, Selected = selected });
        }

        return list;
    }

}

然后在你的视图中你可以像这样使用它们......

@using (Html.BeginForm("Mday", "Metrics", FormMethod.Get))
{
    @Html.DropDownListFor(x => x.SelectedMonth, Model.GetMonthsSelectList())
    @Html.DropDownListFor(x => x.SelectedYear, Model.GetYearsSelectList())

    <input type="submit" />
}
@if (Model.CanShowChart)
{
    <img src="@Url.Action("ValidateChart", new { month = Model.SelectedMonth, year = Model.SelectedYear })" alt="Test"/>
}

然后你的控制器可以减少到这个......

public ActionResult Validate(int month = 0, int year = 0)
{
    //Check if input is valid
    ValidateViewModel model = new ValidateViewModel();
    model.CanShowChart = month != 0 && year != 0;
    model.SelectedMonth = month.ToString();
    model.SelectedYear = year.ToString();

    //Populate years
    model.AvailableYears = Enumerable.Range(2008, DateTime.Now.Year - 2007).Reverse().Select(r => r.ToString());

    //Populate months
    model.AvailableMonths = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };

    return View(model);
}

答案 1 :(得分:1)

Querystring和URL更改在将变量从请求范围传递到服务器时非常有用。我不认为他们是不好的做法。如果一个人正在使用它,那么设计的系统就要回答这个问题。

IMO,像int ChosenMonth这样的变量在VM中很有用,因为它们有助于正确定义系统。 ValidateVM的当前定义并不意味着能够选择一个月和一年的功能,我认为这是对定义系统的隐蔽逻辑。在围绕选择功能编写测试时,它会更有帮助。人们可以测试选择功能,就像它的使用方式一样。

此外,看看是否有任何要求将月份和年份连接到SelectedItem是值得的,例如

class SelectedItem { int year, int month }

答案 2 :(得分:1)

我只会评论代码的验证方面。整个验证方法真的很尴尬。应使用ModelState验证模型。正确的范例是在你的控制器中写这样的东西:

[HttpGet]
public ActionResult MyReport()
{
    ReportModel model = new ReportModel();
    // populate your model in order for all your inputs to show correctly (dropdowns data or whatever)
    return View("[pathToView]/ReportView.cshtml", model);
}

[HttpGet]
public ActionResult ShowMyReport(ReportModel model)
{
    if(ModelState.IsValid)
    {
        // inputs validated, show the report
        [get report data according to the input]
        return View(model);
    }
    // inputs didn't validate. Rerender view and show errors.
    return View("[pathToView]/ReportView.cshtml", model); // should be the same view
}

现在,您可以通过数据注释验证ReportModel,或使用FluentValidation更好的方式验证。所以不,你使用ViewBag的方式 - 表明模型的有效位置 - 根本不是最佳实践。

ModelState是一个包含模型所有错误的对象。您可以像ModelState.AddModelError("[propertyName]","Error Message")一样手动添加错误,然后如果未验证模型,如果@Html.ValidationSummary()某处是视图或@Html.ValidationMessageFor(model => model.PropertyName),则视图中将显示所有错误消息你的观点。 Google周围有关于此的更多信息。有很多关于这个主题的信息。

最佳做法是尽可能减少控制器中的操作,这与您的操作相去甚远。出于这个原因,不鼓励在控制器中向ModelState添加错误。再次,使用FluentValidation,因为它(1)分离了验证的关注点,(2)比数据注释更灵活。