这一直是让我感到困惑的事情,在保持可维护代码的同时,最好的方法。下面的代码为支付网关表单设置月份和年份列表,然后将其分配给类型为 List<SelectListItem>
的变量。
初始操作
PayNowViewModel paymentGateway = new PayNowViewModel();
List<SelectListItem> paymentGatewayMonthsList = new List<SelectListItem>();
List<SelectListItem> paymentGatewayYearsList = new List<SelectListItem>();
for (int i = 1; i <= 12; i++)
{
SelectListItem selectListItem = new SelectListItem();
selectListItem.Value = i.ToString();
selectListItem.Text = i.ToString("00");
paymentGatewayMonthsList.Add(selectListItem);
}
int year = DateTime.Now.Year;
for (int i = year; i <= year + 10; i++)
{
SelectListItem selectListItem = new SelectListItem();
selectListItem.Value = i.ToString();
selectListItem.Text = i.ToString("00");
paymentGatewayYearsList.Add(selectListItem);
}
paymentGateway.ExpiryMonth = paymentGatewayMonthsList;
paymentGateway.ExpiryYear = paymentGatewayYearsList;
return View(paymentGateway);
这是相当多的代码,我发现自己以类似的格式重复此代码,以重新设置下拉列表选项,如果ModelState.IsValid为假,我想返回到视图供用户更正错误。
HttpPost Action - Code
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ConfirmPayment(PayNowViewModel paymentGatewayForm, FormCollection form)
{
if (ModelState.IsValid)
{
// Post processing actions...
return View();
}
else
{
for (int i = 1; i <= 12; i++)
{
SelectListItem selectListItem = new SelectListItem();
selectListItem.Value = i.ToString();
selectListItem.Text = i.ToString("00");
paymentGatewayMonthsList.Add(selectListItem);
}
int year = DateTime.Now.Year;
for (int i = year; i <= year + 10; i++)
{
SelectListItem selectListItem = new SelectListItem();
selectListItem.Value = i.ToString();
selectListItem.Text = i.ToString("00");
paymentGatewayYearsList.Add(selectListItem);
}
form.ExpiryMonth = paymentGatewayMonthsList;
form.ExpiryYear = paymentGatewayYearsList;
return View("MakePayment", form);
}
}
集中此下拉设置代码使其仅在一个地方的最佳方法是什么?目前你会看到很大一部分(for循环),正好重复了两次。具有功能的基本控制器?还是像上面那样重新设置更好?
任何建议表示赞赏!话筒。
向控制器添加一个私有方法(以下代码假定您的ExpiryMonth
和ExpiryYear
属性IEnumerable<SelectListItem>
这就是DropDownListFor()
方法所需的全部内容)
private void ConfigureViewModel(PayNowViewModel model)
{
model.ExpiryMonth = Enumerable.Range(1, 12).Select(m => new SelectListItem
{
Value = m.ToString(),
Text = m.ToString("00")
});
model.ExpiryYear = Enumerable.Range(DateTime.Today.Year, 10).Select(y => new SelectListItem
{
Value = y.ToString(),
Text = y.ToString("00")
});
}
然后在 GET 方法中
public ActionResult ConfirmPayment()
{
PayNowViewModel model = new PayNowViewModel();
ConfigureViewModel(model);
return View(model);
}
和在开机自检方法中
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ConfirmPayment(PayNowViewModel model)
{
if (!ModelState.IsValid)
{
ConfigureViewModel(model);
return View(model);
}
.... // save and redirect (should not be returning the view here)
}
如果下拉选项集是固定的(或者在潜在选项更改后重新编译是正常的),则可以使用枚举来存储选项。
public enum Month {
// if the dropdown is not required, add default value 0
Optional = 0,
[Display(Name = @"Month_January")]
January = 1,
[Display(Name = @"Month_February")]
February = 2,
// etc ..
}
要将其呈现为下拉列表,请使用编辑器模板Enum.cshtml
:
@model Enum
@{
var enumType = ViewData.ModelMetadata.ModelType;
var allValues = Enum.GetValues(enumType).Cast<object>().ToSelectList(Model);
// read any attributes like [Required] from ViewData and ModelMetadata ...
var attributes = new Dictionary<string, object>();
}
@Html.DropDownListFor(m => m, allValues, attributes)
ToSelectList
扩展方法遍历所有枚举值,并将它们转换为 SelectListItems:
public static IList<SelectListItem> ToSelectList<T>(this IEnumerable<T> list) {
return ToSelectList<T>(list, list.FirstOrDefault());
}
public static IList<SelectListItem> ToSelectList<T>(this IEnumerable<T> list, T selectedItem) {
var items = new List<SelectListItem>();
var displayAttributeType = typeof(DisplayAttribute);
foreach (var item in list) {
string displayName;
// multi-language:
// assume item is an enum value
var field = item.GetType().GetField(item.ToString());
try {
// read [Display(Name = @"someKey")] attribute
var attrs = (DisplayAttribute)field.GetCustomAttributes(displayAttributeType, false).First();
// lookup translation for someKey in the Resource file
displayName = Resources.ResourceManager.GetString(attrs.Name);
} catch {
// no attribute -> display enum value name
displayName = item.ToString();
}
// keep selected value after postback:
// assume selectedItem is the Model passed from MVC
var isSelected = false;
if (selectedItem != null) {
isSelected = (selectedItem.ToString() == item.ToString());
}
items.Add(new SelectListItem {
Selected = isSelected,
Text = displayName,
Value = item.ToString()
});
}
return items;
}
要支持多种语言,请为显示名称键添加翻译,例如 "Month_January"
,到资源文件。
现在,设置代码已经使用一些反射魔法抽象出来,创建新的视图模型是轻而易举的:>
public class PayNowViewModel {
// SelectListItems are only generated if this gets rendered
public Month ExpiryMonth { get; set; }
}
// Intial Action
var paymentGateway = new PayNowViewModel();
return View(paymentGateway);
// Razor View: call the EditorTemplate
@Html.EditorFor(m => m.ExpiryMonth)
请注意,在编辑器模板中,Model
作为选定项传递给ToSelectList
。回发后,模型将保留当前选定的值。因此,即使您只是在控制器中出现错误后返回模型,它也会保持选中状态:
// HttpPost Action
if (!ModelState.IsValid) {
return View("MakePayment", paymentGatewayForm);
}
我们花了一些时间来提出这个解决方案,功劳归萨拉蒂巴团队。