我目前正在开展一个项目,其中我们有许多类似的模型。我们使它们都继承自相同的基本模型,我们还制作了一个通用控制器(基本的东西,如列表,添加,编辑等)处理基本模型。它实际上是有效的,并且使我们的事情变得简单,但是下拉的问题很小。
如果我可以使用动作将数据从控制器传递到视图,那么如果我没有详细介绍,那么对我来说这会让事情变得非常简单。像这样:
@Html.DropDownListFor(model => model.Xtypechild, Action("GetList", "XController"))
行动GetList
返回IEnumerable<SelectListItem>
这样我只需编写一次代码并将其用于许多不同的控制器。
我知道这听起来很疯狂,但如果我尝试使用一种更传统的方法来做到这一点,我会再次使用冗余代码,并且会失去我们从继承控制器中获得的一些优势。
我现在能看到的唯一选择是让GetList
返回包含所有项目的JSON,并使用Ajax / JS填充下拉列表。我也可以做一个返回包含下拉列表的局部视图的动作,但我不确定这是不是一个好主意。
感谢所有输入。最后,我采用了以下解决方案:
我们有一个服务类来处理模型的数据库操作。我只是添加一个为基本服务类生成IEnumerable<SelectListItem>
的方法。现在我通过ViewBag传递项目以进行下拉(虽然它强制我在视图中使用之前将它们转换回IEnumerable)。我将它们分配给继承控制器的重写构造函数中的ViewBag。
如果这个解决方案适用于我们的项目,我最好咨询代码的原作者,尽管他现在正在度假。现在这将是必须的。从好的方面来说,这种方法产生的代码很少,因此应该很容易替换(如果必须这样做的话)。
代码进化了一点,现在我在视图中有这样的东西(注意int?
转换是存在的,因为属性是int类型,否则它将不起作用):
Html.DropDownListFor(
model => model.XId,
new SelectList((List<X>)ViewBag.Xs, "Id", "Name", (Model != null ? (int?)Model.XId : null)),
" -- select -- ",
null)
在控制器的重写构造函数中有类似的东西:
ViewBag.Xs = db.Xs.ToList();
这绝不是在剃刀中实施下拉菜单的最佳方式,但这是在项目中实施它们的最佳方式。原因是:没有时间让项目使用ViewModels并覆盖基本控制器的所有操作来处理它们(以及为这些操作中的下拉列表提供数据)。
代码进一步发展。如果数据仅在少数几个视图中使用,那么在控制器构造函数中执行查询肯定不是一个好主意。所以现在构造函数包含相当于:
ViewBag.Xs = (IQueryable<X>)db.Xs;
观点:
Html.DropDownListFor(
model => model.XId,
new SelectList((IQueryable<X>)ViewBag.Xs, "Id", "Name", (Model != null ? (int?)Model.XId : null)),
" -- select -- ",
null)
这样,只有在视图实际需要数据时才会执行查询。
答案 0 :(得分:1)
下拉列表的项目绝对应该在您的ViewModel中。我不认为在派生的ViewModel中创建选择列表是错误的。它可能看起来像重复的代码,但至少它清楚你做了什么。您可以在基本控制器中创建一些不错的帮助程序,负责将实体映射到SelectListItem
。
如果您真的想要通用解决方案,可以在基础模型中创建一个方法:
public class BaseViewModel
{
// Other properties..
public IEnumerable<SelectListItem> GetListFromAction(string actionName, string controllerName)
{
// return the select list from the action.
}
}
并在DropDownListFor()
:
@Html.DropDownListFor(model => model.Xtypechild, Model.GetListFromAction("GetList", "XController"))
答案 1 :(得分:1)
我当然不同意在视图模型上使用方法来填充SelectList
,即使它正在调用控制器。原因很简单:控制器负责填充模型,而不是相反。
处理此问题的最佳方法是在控制器上设置一个可以为您构建列表的方法。由于这是一项非常常见的任务,因此建议多个控制器需要访问该功能,因此创建自己的BaseController
所有控制器都可以从中获取,这是最佳选择。
考虑到这一点,这里是一个BaseController
,其中包含我编写的用于构建任何SelectList
的通用方法:
public class BaseController : Controller
{
[NonAction]
public SelectList BuildSelectList<TSource>(IEnumerable<TSource> source,
Expression<Func<TSource, int>> valueKey, Expression<Func<TSource, string>> textKey,
object selectedValue = null)
{
var selectedValueKey = ((MemberExpression)(MemberExpression)valueKey.Body).Member.Name;
var selectedTextKey = ((MemberExpression)(MemberExpression)textKey.Body).Member.Name;
return new SelectList(source, selectedValueKey, selectedTextKey, selectedValue);
}
}
请注意使用NonActionAttribute
标记方法。这可以防止将该方法作为操作调用。下面是一个如何使用它的简单示例。
public class StudentViewModel
{
public int Id { get; set; }
public string Name { get; set; }
[Display(Name = "Course")]
public int CourseId { get; set; }
public SelectList Courses { get; set; }
}
CourseId
代表所选课程。
public class Course
{
public int Id { get; set; }
public string Name { get; set; }
}
// Notice how we're deriving from BaseController
public class HomeController : BaseController
{
public ActionResult Index()
{
var model = new StudentViewModel();
model.Id = 1;
model.Name = "John";
model.Courses = BuildSelectList(this.courses,
m => m.Id, m => m.Name);
return View(model);
}
[HttpPost]
public ActionResult Index(StudentViewModel model)
{
if (ModelState.IsValid)
{
return RedirectToAction("Success");
}
// The model wasn't valid, so repopulate the dropdown
model.Courses = BuildSelectList(this.courses, m => m.Id,
m => m.Name, model.CourseId);
return View(model);
}
}
然后,您会在视图中显示下拉列表:
@Html.DropDownListFor(m => m.CourseId, Model.Courses)
答案 2 :(得分:0)
public ActionResult Index()
{
var directors = new Collection
{
new Director {Id = 1, Name = "David O. Russell"},
new Director {Id = 2, Name = "Steven Spielberg"},
new Director {Id = 3, Name = "Ben Affleck"}
};
var selectList = new SelectList(directors, "Id", "Name");
var vm = new ViewModel {DirectorSelectList = selectList};
return View(vm);
}
@Html.DropDownListFor(viewModel => viewModel.DirectorId, directorList)
如果您想查看示例,请输入链接。
http://peternewhook.com/2013/02/asp-net-mvc-selectlist-selectedvalue-dropdownlistfor/
答案 3 :(得分:0)
我有BaseController
继承自Controller
,然后将GenerateStateOptions
等方法放入BaseController
。
它可能看起来像这样:
public IEnumerable<SelectListItem> GenerateStateOptions()
{
var items = new List<SelectListItem>();
var states = New List<State>(); // get states from your DB or wherever
foreach(var state in states)
{
items.Add(new SelectListItem()); // set your properties for the SLI
}
return items;
}
然后在您的Controller中,您可以执行以下操作:
... // additional ActionMethod code here
model.States = GenerateStateOptions();