如何在 Asp.net MVC 和 entity Framework 中分页时应用过滤器?
How to I apply filter while paginating in Asp.net MVC and entity Framework?
我有一个使用 ASP.NET MVC 框架编写的网络应用程序。在我的 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
请求。所以在 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);
}
这也能正常工作,除非用户更改了页面,然后过滤器就停止了。
这是我的演示文稿 class 的样子
public class Presenter
{
public IPagedList<Task> Tasks { get; set; }
public TasksFiltersViewModel Form { get; set; }
public int PageNumber { get; set; }
public IEnumerable<SelectListItem> Statuses { get; set; }
}
如何在保留过滤器的同时允许用户使用页面?
这是我的过滤器 VM
public class TasksFiltersViewModel
{
public int Status { get; set; }
}
视图看起来像这样
@using (Html.BeginForm("Index", "Tasks", FormMethod.Post, new { @class = "form-horizontal" }))
{
@Html.AntiForgeryToken()
<div class="form-group">
@Html.LabelFor(m => m.Form.Status, new { @class = "control-label col-sm-3" })
<div class="col-sm-9">
@Html.DropDownListFor(m => m.Form.Status, Model.Statuses, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Form.Status, "", new { @class = "text-danger" })
</div>
</div>
<div class="row">
<div class="col-sm-9 col-md-push-3">
<div>
<button type="submit" class="btn btn-default">Filter</button>
</div>
</div>
</div>
}
foreach (var task in Model.Tasks)
{
<tr>
<td>@task.Name</td>
<td>@task.Type</td>
<td>@Html.ActionLink("Edit", "Details", "Task", new { @id = task.Id }, new { @class = "btn btn-primary btn-sm" })</td>
</tr>
}
@Html.PagedListPager(Model.Tasks, id => Url.Action("Index", new { id }))
我认为更好的方法应该是将 ViewBag
中的过滤器传回视图。
您可以制作如下内容:
@Html.PagedListPager(
Model.Tasks, id =>
Url.Action("Index", new { id,
Status = ViewBag.Status , AnotherFilterValue = ViewBag.AnotherFilterValue, ... }))
但请记住测试 ViewBag.Status
是否存在空值。如果它确实有值,请将其放入路由参数列表中,否则设置默认值 ActionLink
.
然后在 POST 操作中,您期望一个可为空的整数,如下所示:
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);
}
您的表单需要 post 返回 GET 方法,并且该方法需要包含过滤器属性的参数。您在视图中的 PagedListPager
代码还需要包含这些过滤器属性,以便在您导航到 next/previous 页面时保留它们。注意Index()
POST方法不用,可以删掉
让您的模型包含过滤器属性的复杂对象以及绑定时的额外复杂性,因此首先将您的模型更改为
public class Presenter
{
public IPagedList<Task> Tasks { get; set; }
public int? Status { get; set; } // note nullable
... // add any other properties of TasksFiltersViewModel
public int PageNumber { get; set; }
public IEnumerable<SelectListItem> 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<Task>
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
有两种方法。
快捷方式:Cookie。
只需为过滤器选项设置一个 cookie。在需要过滤的操作上,您只需要读取cookie并进行相应的过滤即可。
我不喜欢 cookie 和会话,并且会避免使用它们。但有时它可能会成为你所需要的。
使用 GET 参数
在您的示例中,您使用了 POST
进行过滤。每次单击 link,它都会触发 GET
请求,而不是 POST
。所以过滤不会发生。棘手的部分是确保每次都设置 GET
参数。可以创建类似于 Html.Action()
的自定义扩展。如果您要在多个操作中检查过滤器选项,请考虑使用操作 Filter
.
我有一个使用 ASP.NET MVC 框架编写的网络应用程序。在我的 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
请求。所以在 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);
}
这也能正常工作,除非用户更改了页面,然后过滤器就停止了。
这是我的演示文稿 class 的样子
public class Presenter
{
public IPagedList<Task> Tasks { get; set; }
public TasksFiltersViewModel Form { get; set; }
public int PageNumber { get; set; }
public IEnumerable<SelectListItem> Statuses { get; set; }
}
如何在保留过滤器的同时允许用户使用页面?
这是我的过滤器 VM
public class TasksFiltersViewModel
{
public int Status { get; set; }
}
视图看起来像这样
@using (Html.BeginForm("Index", "Tasks", FormMethod.Post, new { @class = "form-horizontal" }))
{
@Html.AntiForgeryToken()
<div class="form-group">
@Html.LabelFor(m => m.Form.Status, new { @class = "control-label col-sm-3" })
<div class="col-sm-9">
@Html.DropDownListFor(m => m.Form.Status, Model.Statuses, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Form.Status, "", new { @class = "text-danger" })
</div>
</div>
<div class="row">
<div class="col-sm-9 col-md-push-3">
<div>
<button type="submit" class="btn btn-default">Filter</button>
</div>
</div>
</div>
}
foreach (var task in Model.Tasks)
{
<tr>
<td>@task.Name</td>
<td>@task.Type</td>
<td>@Html.ActionLink("Edit", "Details", "Task", new { @id = task.Id }, new { @class = "btn btn-primary btn-sm" })</td>
</tr>
}
@Html.PagedListPager(Model.Tasks, id => Url.Action("Index", new { id }))
我认为更好的方法应该是将 ViewBag
中的过滤器传回视图。
您可以制作如下内容:
@Html.PagedListPager(
Model.Tasks, id =>
Url.Action("Index", new { id,
Status = ViewBag.Status , AnotherFilterValue = ViewBag.AnotherFilterValue, ... }))
但请记住测试 ViewBag.Status
是否存在空值。如果它确实有值,请将其放入路由参数列表中,否则设置默认值 ActionLink
.
然后在 POST 操作中,您期望一个可为空的整数,如下所示:
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);
}
您的表单需要 post 返回 GET 方法,并且该方法需要包含过滤器属性的参数。您在视图中的 PagedListPager
代码还需要包含这些过滤器属性,以便在您导航到 next/previous 页面时保留它们。注意Index()
POST方法不用,可以删掉
让您的模型包含过滤器属性的复杂对象以及绑定时的额外复杂性,因此首先将您的模型更改为
public class Presenter
{
public IPagedList<Task> Tasks { get; set; }
public int? Status { get; set; } // note nullable
... // add any other properties of TasksFiltersViewModel
public int PageNumber { get; set; }
public IEnumerable<SelectListItem> 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<Task>
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
有两种方法。
快捷方式:Cookie。
只需为过滤器选项设置一个 cookie。在需要过滤的操作上,您只需要读取cookie并进行相应的过滤即可。
我不喜欢 cookie 和会话,并且会避免使用它们。但有时它可能会成为你所需要的。
使用 GET 参数
在您的示例中,您使用了 POST
进行过滤。每次单击 link,它都会触发 GET
请求,而不是 POST
。所以过滤不会发生。棘手的部分是确保每次都设置 GET
参数。可以创建类似于 Html.Action()
的自定义扩展。如果您要在多个操作中检查过滤器选项,请考虑使用操作 Filter
.