【问题标题】:How can I avoid using a ViewBag in MVC4 to pass query values between controller and view如何避免在 MVC4 中使用 ViewBag 在控制器和视图之间传递查询值
【发布时间】:2013-03-17 23:03:47
【问题描述】:

我读到它是generally a bad practice to use ViewBags,而且混合 ViewBag 和 ViewModel 也不好,但我使用一些 ViewBags 在我的控制器和视图之间传递 sort order 参数,尽管我可以在 @987654323 上创建属性@ 对于视图,我首先需要这些值才能创建模型,所以看不到如何解决这个问题。

控制器

public ActionResult Index(string sortOrder, string searchString, 
                          string currentFilter, int? page, 
                          bool? includeComplete)
{

 ViewBag.CurrentSort = sortOrder;
 ViewBag.NameSortParam = string.IsNullOrWhiteSpace(sortOrder) ? "Name desc" : "";
 ViewBag.DateSortParam = sortOrder == "Date" ? "Date desc" : "Date";

 if (Request.HttpMethod == "GET")
     searchString = currentFilter;
 else
     page = 1;

 ViewBag.CurrentFilter = searchString;
 bool showCompleted = (includeComplete == null || includeComplete == false)
                        ? false : true;
 ViewBag.IncludeCompleted = showCompleted;

 int pageNumber = (page ?? 1);

 var query = Session.QueryOver<ToDo>();

 if (!string.IsNullOrWhiteSpace(searchString))
     query = query.WhereRestrictionOn(td => td.TaskName)
                      .IsInsensitiveLike(string.Format("%{0}%", searchString));

 if (!showCompleted)
     query.And(td => td.IsComplete == false);

 switch (sortOrder)
 {
    case "Name desc":
      query = query.OrderBy(td => td.TaskName).Desc;
      break;
        case "Date":
      query = query.OrderBy(td => td.DueDate).Asc;
      break;
    case "Date desc":
      query = query.OrderBy(td => td.DueDate).Desc;
      break;
    default:
      query = query.OrderBy(td => td.TaskName).Asc;
      break;
 }

 var result = query.Fetch(p=>p.Priority).Eager
           .Fetch(s=>s.Staff).Eager
           .List();

 var viewModel = AutoMapper.Mapper.Map<IEnumerable<ToDo>, 
                     IEnumerable<IndexToDoViewModel>>(result)
                     .ToPagedList(pageNumber, PageSize);
 return View(viewModel);

}

然后在我看来,我将使用 ViewBag 将值传递回控制器,例如我需要拥有的 sortOrder,然后才能将它应用到构成方法主体的 NHibernate 查询中,例如:

@Html.ActionLink("Name", "Index", new {sortOrder=ViewBag.NameSortParam, 
                                      includeComplete = ViewBag.IncludeCompleted})

我的视图模型

public class IndexToDoViewModel
{
    [DataType(DataType.Date)]
    [DisplayFormat(DataFormatString = "{0:dd MMM yyyy}")]
    [DisplayName("Date Due")]
    public DateTime? DueDate { get; set; }

    public Guid Id { get; set; }

    [DisplayName("Is Complete")]
    public bool IsComplete { get; set; }

    [DataType(DataType.MultilineText)]
    public String Notes { get; set; }

    public string Priority { get; set; }

    public string Staff {get; set;}

    [DisplayName("Name")]
    public String TaskName { get; set; }

    // These would potentially replace my ViewBag?
    public string CurrentSort { get; set; }
    public string CurrentFilter { get; set; }
    public string NameSortParam { get; set; }
    public string DateSortParam { get; set; }
    public bool IncludeCompleted { get; set; }

}

【问题讨论】:

  • 为什么不在 ViewModel 中包含 SortOrder?

标签: asp.net-mvc razor asp.net-mvc-4


【解决方案1】:

然后在我看来,我将使用 ViewBag 将值传递回 控制器

不,您将值传递给下一个控制器。当控制器的新实例将处理请求时,这些值将(可能)在浏览器往返后使用。

由于您没有将任何内容传递回当前控制器,因此无需使用ViewBag。如果您愿意,可以将值放入模型中。


即使您将数据从视图发送到控制器(这是非常不寻常的),使用模型仍然可以实现。示例:

MyModel viewModel = new MyModel();
ActionResult result = View(viewModel);

// here you can access anything that the view would put in the model

return result;

当然,您不能使用控制器中视图中的任何值来创建模型,因为您必须在视图之前创建模型。如果您需要视图中的任何数据来为其获取数据,您可以将其作为方法放入模型而不是控制器中。

【讨论】:

  • 我不清楚这将如何工作;例如在ActionLink 代码中,它获取了“名称”(显示值)、“索引”(控制器),然后设置了其他属性...如何在我的 ViewModel 上设置这些属性?
  • @SimonMartin:你可以在你的模型中放任何东西。目前,您有一个 Map 对象作为模型,相反,您将创建一个自定义对象,该对象具有 Map 对象作为属性,以及您需要的任何其他数据作为属性。
  • 但是视图如何将模型发送到带有值的控制器,在我的“名称”排序示例中,在显示值和方法名称之后我的ActionLink 中会出现什么 - 其中我目前已经设置了 ViewBag 属性?
  • @SimonMartin:视图没有将模型发送到控制器,相反。 ActionLink 中的内容进入一个 URL,该 URL 用于发出另一个请求,该请求将在未来最终到达控制器中,而不是用于创建此响应的控制器中。您可以在ActionLink 中使用您想要的任何值,具体取决于您希望将来的控制器如何对数据进行排序。如果您希望它像当前响应中的数据一样排序,您将使用与用于获取当前模型的数据相同的值。
  • 对;我认为我明白了。我的 ViewModel 中的属性可以像 @Html.TextBox("SearchFilter", Model.SearchFilter) 这样访问,ActionResult 参数变成了 IndexToDoViewModel,然后 logic 从视图中删除并存在于控制器和模型中。然而,正如@Oliver 所说,我需要抽象一个层并将我当前的 ViewModel 包装为一个属性,将其公开为 IEnumerable 以使 PagedList 接口快乐。
【解决方案2】:

我建议为您创建的每个视图创建一个 ViewModel,因为您很可能需要将更多信息传递给您的视图。

在这种情况下,您将拥有一个具有分页列表数据属性的 ViewModel,并且您可以为 SortOrder 添加另一个属性。

例如:

public class MyViewModel
{
    public IEnumerable<ToDo> TodoList { get; set; }
    public string SortOrder { get; set; }
}

查看 SO 问题:ViewModel Best Practices

【讨论】:

  • 这就是我的想法,但我需要在控制器顶部提供SortOrder 属性,但只有在我初始化后才能在控制器底部使用它在将视图模型返回给视图之前。
  • @SimonMartin:不,模型中的排序顺序就是您输入的值。要对该页面的数据进行排序,您需要从请求的 URL 获取的 上一页 页面中的排序顺序,即您的操作方法中的 sortOrder 参数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-19
  • 1970-01-01
  • 1970-01-01
  • 2012-11-16
相关资源
最近更新 更多