0

我目前正在开展一个项目,其中我们有许多类似的模型。我们让它们都继承自同一个基本模型,我们还制作了一个通用控制器(基本的东西,如列表、添加、编辑等)来处理基本模型。它实际上可以工作并使我们的事情变得容易,但我对下拉菜单有一个小问题。

无需详细说明,如果我可以使用操作将数据从控制器传递到视图,这对我来说真的很容易。像这样的东西:

@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();

这绝对不是在 Razor 中实现下拉菜单的最佳方式,但它是在项目中实现它们的最佳方式。这样做的原因是:没有时间让项目使用 ViewModel 并覆盖基本控制器的所有操作来处理它们(以及为这些操作中的下拉菜单提供数据)。


代码进一步发展。如果数据仅在少数视图中使用,那么在控制器构造函数中执行查询肯定不是一个好主意。所以现在构造函数包含等价于:

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)

这样查询仅在视图实际需要数据时执行。

4

4 回答 4

1

下拉列表的项目绝对应该在您的 ViewModel 中。我不认为在派生的 ViewModel 中创建选择列表是错误的。它可能看起来像重复的代码,但至少很清楚你在做什么。您可以在基本控制器中创建一些不错的助手,负责将实体映射到SelectListItems。

如果你真的想要一个通用的解决方案,你可以在你的基础模型中创建一个方法:

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"))
于 2013-10-07T12:14:05.693 回答
1

我当然不同意在视图模型上有一个方法来填充SelectList,即使它正在调用控制器。原因很简单:填充模型是控制器的责任,而不是相反。

处理这个问题的最好方法是在你的控制器上有一个可以为你构建列表的方法。由于这是一项非常常见的任务,这表明多个控制器需要访问该功能,因此创建自己的BaseController所有控制器派生的控制器是可行的方法。

考虑到这一点,这是BaseController我编写的用于构建 any 的通用方法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)
于 2013-10-07T14:29:24.650 回答
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/

于 2013-10-07T13:00:56.560 回答
0

我有一个BaseController继承自然后将方法ControllerGenerateStateOptions.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();
于 2013-10-07T13:56:54.950 回答