Come applicare il filtro durante la impaginazione in Asp.net MVC e Entity Framework?

Ho un’app Web scritta utilizzando il framework ASP.NET MVC. Nel mio Homecontroller ho un’azione chiamata Index che risponde a una richiesta Get . In questa azione, creo pagine utilizzando la libreria IPagedList per suddividere i record in più pagine. Il mio [email protected] è simile a questo

 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); } } 

Ho anche un’azione denominata Index che risponde alla richiesta Post che applica alcuni filtri. Quindi nella richiesta Post faccio qualcosa di simile

 [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); } 

Anche questo funziona bene a meno che l’utente non abbia cambiato la pagina, quindi i filtri sono riposati.

Ecco come appare il mio corso di presentazione

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

Come posso consentire agli utenti di utilizzare le pagine mantenendo i filtri?

Ecco i miei filtri VM

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

La vista sembra così

 @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 }))

Il modulo deve postare nuovamente al metodo GET e tale metodo deve includere parametri per le proprietà del filtro. Il codice di PagedListPager nella vista deve inoltre includere tali proprietà del filtro in modo che vengano conservate quando si passa alla pagina successiva / precedente. Si noti che il metodo POST Index() non viene utilizzato e può essere eliminato.

Avere il tuo modello contiene un object complesso alle proprietà del filtro e una maggiore complessità durante il binding, quindi inizia cambiando il tuo modello in

 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; } } 

Quindi cambiare il metodo Index() in

 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 ); } 

e cambia la vista in

 // 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 

Penso che un modo migliore dovrebbe tornare alla visualizzazione dei filtri in ViewBag . Puoi fare qualcosa di simile qui sotto:

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

Ma tieni a mente di testare ViewBag.Status per il valore nullo. Se ha un valore inserito nell’elenco dei parametri del percorso, impostare un ActionLink predefinito.

Quindi all’interno dell’azione POST ti aspetti un int nullo come qui sotto:

 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); } 

Due modi per farlo.

Il modo rapido: i cookie.

Basta impostare un cookie per le opzioni di filtro. Sulle azioni in cui è richiesto il filtraggio, tutto ciò che devi fare è leggere i cookie e filtrare di conseguenza.
Non mi piacciono i cookie e le sessioni e li eviterei. Ma a volte potrebbe rivelarsi ciò di cui hai bisogno.

Usa i parametri GET

Nel tuo esempio hai usato POST per filtrare. Ogni volta che fai clic su un link, viene generata una richiesta GET , non un POST . Quindi il filtraggio non avviene. La parte difficile è assicurarsi che i parametri GET siano impostati ogni volta. Potrebbe creare un’estensione personalizzata simile a Html.Action() per iniziare. Se controllerai le opzioni del filtro in più azioni, considera l’utilizzo di un’azione Filter .