如何在Asp.net MVC和entity framework中进行分页时应用filter?

我有一个使用ASP.NET MVC框架编写的Web应用程序。 在我的Homecontroller我有一个名为Index的动作,它响应Get请求。 在此操作中,我使用IPagedList库创建页面以将记录分成多个页面。 我的Index@HttpGet看起来像这样

 public ActionResult Index(int? id) { using(var connection = new Context()) { int pageNumber = (id ?? 1); var presenter = new Presenter { Presenter = pageNumber, Tasks = connection.Tasks.ToPagedList(pageNumber, 30), Form = new TasksFiltersViewModel() } return View(presenter); } } 

我还有一个名为Index的操作,它响应Post请求,该请求应用了一些filter。 所以在Post请求中我做了这样的事情

 [HttpPost] [ValidateAntiForgeryToken] public ActionResult Index(Presenter model) { int pageNumber = (id ?? 1); if (ModelState.IsValid) { using(var connection = new Context()) { model.Tasks = connection.Tasks .Where(task => task.Status == 5) .ToPagedList(pageNumber, 30); } } return View(model); } 

这也可以正常工作,除非用户更改页面然后filterrest。

这是我的演示类的样子

 public class Presenter { public IPagedList Tasks { get; set; } public TasksFiltersViewModel Form { get; set; } public int PageNumber { get; set; } public IEnumerable Statuses { get; set; } } 

如何在保留filter的同时允许用户使用这些页面?

这是我的filterVM

 public class TasksFiltersViewModel { public int Status { get; set; } } 

视图看起来像这样

 @using (Html.BeginForm("Index", "Tasks", FormMethod.Post, new { @class = "form-horizontal" })) { @Html.AntiForgeryToken() 
@Html.LabelFor(m => m.Form.Status, new { @class = "control-label col-sm-3" })
@Html.DropDownListFor(m => m.Form.Status, Model.Statuses, new { @class = "form-control" }) @Html.ValidationMessageFor(m => m.Form.Status, "", new { @class = "text-danger" })
} foreach (var task in Model.Tasks) { @task.Name @task.Type @Html.ActionLink("Edit", "Details", "Task", new { @id = task.Id }, new { @class = "btn btn-primary btn-sm" }) } @Html.PagedListPager(Model.Tasks, id => Url.Action("Index", new { id }))

您的表单需要回发到GET方法,该方法需要包含filter属性的参数。 视图中的PagedListPager代码还需要包含这些filter属性,以便在导航到下一页/上一页时保留它们。 请注意,不使用Index() POST方法,可以删除它。

让模型包含filter属性的复杂对象和绑定时的额外复杂性,因此首先将模型更改为

 public class Presenter { public IPagedList Tasks { get; set; } public int? Status { get; set; } // note nullable ... // add any other properties of TasksFiltersViewModel public int PageNumber { get; set; } public IEnumerable Statuses { get; set; } } 

然后将Index()方法更改为

 public ActionResult Index(int? id, int? status) // add any other parameters your filtering on { int pageNumber = (id ?? 1); var tasks = db.Tasks; // IQueryable if (status.HasValue) { tasks = tasks.Where(x => x.Status == status.Value) } if (otherParametersHaveValue) { tasks = tasks.Where(....); } Presenter model = new Presenter() { PageNumber = id ?? 1, Status = status, .... // set any other filter properties from the parameters Statuses = new SelectList(...), Tasks = tasks.ToPagedList(pageNumber, 30) }; return View(model ); } 

并将视图更改为

 // Make the form a GET @using (Html.BeginForm("Index", "Tasks", FormMethod.Get, new { @class = "form-horizontal" })) { .... // Modify expression based on revised model properties @Html.DropDownListFor(m => m.Status, Model.Statuses, ...) } .... // Add filter parameters to url so they are retained @Html.PagedListPager(Model.Tasks, id => Url.Action("Index", new { id, status = Model.Status })) // add other filter properties as required 

我认为更好的方法应该是回到查看ViewBag的filter。 你可以做如下的事情:

 @Html.PagedListPager( Model.Tasks, id => Url.Action("Index", new { id, Status = ViewBag.Status , AnotherFilterValue = ViewBag.AnotherFilterValue, ... })) 

但请记住测试ViewBag.Status的null值。 如果它确实有一个值放在路由参数列表中,则设置默认的ActionLink

然后在POST操作中你期望一个可以为null的int,如下所示:

 public ActionResult Index(int? id, int? status, ...) { int pageNumber = (id ?? 1); if (ModelState.IsValid) { using(var connection = new Context()) { if(status != null) { ViewBag.Status = status.value; model.Tasks = connection.Tasks .Where(task => task.Status == status.value) .ToPagedList(pageNumber, 30); } else { model.Tasks = connection.Tasks .ToPagedList(pageNumber, 30); } } } return View(model); } 

两种方法可以做到这一点。

快捷方式:Cookies。

只需为filter选项设置一个cookie。 在需要过滤的操作上,您需要做的就是读取cookie并相应地过滤。
我不喜欢cookie和会话,并会避免它们。 但有时它可能会成为你需要的东西。

使用GET参数

在您的示例中,您使用POST进行过滤。 每次单击链接时,它都会触发GET请求,而不是POST 。 所以过滤不会发生。 棘手的部分是确保每次都设置GET参数。 可以创建一个类似于Html.Action()的自定义扩展。 如果您要在多个操作中检查filter选项,请考虑使用操作Filter