我希望这是一个正确的问题。如果没有,请告知适当的位置。
意图:用户可以从下拉列表中选择年份和月份。提交表单时,将生成一个图表,以根据选择显示信息。
实施:我的控制器为每个下拉列表创建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
变量的正确方法是什么?
我已经设法让这段代码进入工作状态,但我真的想学习如何以正确的方式做到这一点。
答案 0 :(得分:3)
首先,如果它有效,那肯定不是错误的方式。它可能不是最好的方式,但并没有错。
以下是我要改变的一些事情,你可以决定是否要使用它们......
1)我不会使用ViewBag
,只需向ViewModel添加其他属性,例如bool CanShowChart
。然后,您可以在View逻辑中简单地测试它
2)为string SelectedMonth
和string 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)比数据注释更灵活。