使用操作 C# MVC 传递要查看的数据
本文关键字:数据 操作 MVC | 更新日期: 2023-09-27 18:34:18
我目前正在做一个项目,其中我们有许多类似的模型。我们使它们都继承自相同的基本模型,我们还制作了一个通用控制器(列表、添加、编辑等基本内容(来处理基本模型。它实际上有效并使我们的工作变得容易,但我在下拉菜单方面遇到了小问题。
无需详细介绍,如果我可以使用操作将数据从控制器传递到查看,那对我来说会变得非常容易。 像这样:
@Html.DropDownListFor(model => model.Xtypechild, Action("GetList", "XController"))
操作GetList
返回的位置IEnumerable<SelectListItem>
这样,我只需编写一次代码,并将其用于许多不同的控制器。
我知道这听起来很疯狂,但是如果我尝试使用一种更传统的方法来做到这一点,我最终会再次得到冗余代码,并且会失去我们从继承控制器中获得的一些优势。
我现在能看到的唯一选择是使用所有项目返回 JSON GetList
并使用 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();
这绝对不是在 razor 中实现下拉列表的最佳方式,但它是在项目中实现它们的最佳方式。这样做的原因是:没有时间让项目使用 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)
这样,查询仅在视图实际需要数据时才执行。
的项目绝对应该在你的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"))
我当然不同意在视图模型上使用方法来填充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)
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/
我有一个继承自Controller
的BaseController
,然后将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;
}
然后在控制器中,您可以执行以下操作:
... // additional ActionMethod code here
model.States = GenerateStateOptions();