Utilisation D'une PagedList avec un ViewModel ASP.Net MVC

j'essaie d'utiliser une PagedList dans mon ASP.Net application et j'ai trouvé cet exemple sur le site de Microsoft http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/sorting-filtering-and-paging-with-the-entity-framework-in-an-asp-net-mvc-application

Comment est-il possible d'utiliser une liste de pages dans une situation complexe qui utilise un modèle de vue? J'essaie d'ajouter une PagedList sans succès à L'exemple D'instructeur affiché ici: http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/reading-related-data-with-the-entity-framework-in-an-asp-net-mvc-application

le problème est que le ViewModel est composé par des classes et non par des champs simples, donc je ne peux pas convertir le résultat avec la méthode ToPageList ().

C'est la structure du modèle de vue:

using System.Collections.Generic;
using ContosoUniversity.Models;

namespace ContosoUniversity.ViewModels
{
    public class InstructorIndexData
    {
        public IEnumerable<Instructor> Instructors { get; set; }
        public IEnumerable<Course> Courses { get; set; }
        public IEnumerable<Enrollment> Enrollments { get; set; }
    }
}

je dois joindre les trois tables dans le modèle de vue et afficher le résultat dans une Vue.

21
demandé sur ltedone 2014-08-04 22:37:14

5 réponses

pour toute personne qui essaie de le faire sans modifier vos modèles de vue et ne pas charger tous vos enregistrements à partir de la base de données.

dépôt

    public List<Order> GetOrderPage(int page, int itemsPerPage, out int totalCount)
    {
        List<Order> orders = new List<Order>();
        using (DatabaseContext db = new DatabaseContext())
        {
            orders = (from o in db.Orders
                      orderby o.Date descending //use orderby, otherwise Skip will throw an error
                      select o)
                      .Skip(itemsPerPage * page).Take(itemsPerPage)
                      .ToList();
            totalCount = db.Orders.Count();//return the number of pages
        }
        return orders;//the query is now already executed, it is a subset of all the orders.
    }

contrôleur

    public ActionResult Index(int? page)
    {
        int pagenumber = (page ?? 1) -1; //I know what you're thinking, don't put it on 0 :)
        OrderManagement orderMan = new OrderManagement(HttpContext.ApplicationInstance.Context);
        int totalCount = 0;
        List<Order> orders = orderMan.GetOrderPage(pagenumber, 5, out totalCount);
        List<OrderViewModel> orderViews = new List<OrderViewModel>();
        foreach(Order order in orders)//convert your models to some view models.
        {
            orderViews.Add(orderMan.GenerateOrderViewModel(order));
        }
        //create staticPageList, defining your viewModel, current page, page size and total number of pages.
        IPagedList<OrderViewModel> pageOrders = new StaticPagedList<OrderViewModel>(orderViews, pagenumber + 1, 5, totalCount);
        return View(pageOrders);
    }

Vue

@using PagedList.Mvc;
@using PagedList; 

@model IPagedList<Babywatcher.Core.Models.OrderViewModel>

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>
<div class="container-fluid">
    <p>
        @Html.ActionLink("Create New", "Create")
    </p>
    @if (Model.Count > 0)
    {


        <table class="table">
          <tr>
            <th>
                @Html.DisplayNameFor(model => model.First().orderId)
            </th>
            <!--rest of your stuff-->
        </table>

    }
    else
    {
        <p>No Orders yet.</p>
    }
    @Html.PagedListPager(Model, page => Url.Action("Index", new { page }))
</div>

Bonus

Faire en haut d'abord, puis peut-être utiliser cette!

puisque cette question concerne les modèles (view), je vais vous donner une petite solution qui sera non seulement utile pour la pagination, mais aussi pour le reste de votre application si vous voulez garder vos entités séparées, seulement utilisées dans le dépôt, et que le reste de l'application traite des modèles (qui peuvent être utilisés comme modèles de vue).

dépôt

dans votre référentiel d'ordre (dans mon cas), ajouter un méthode pour convertir un modèle:

public static OrderModel ConvertToModel(Order entity)
{
    if (entity == null) return null;
    OrderModel model = new OrderModel
    {
        ContactId = entity.contactId,
        OrderId = entity.orderId,
    }
    return model;
}

en dessous de votre classe de dépôt, ajoutez ceci:

public static partial class Ex
{
    public static IEnumerable<OrderModel> SelectOrderModel(this IEnumerable<Order> source)
    {
        bool includeRelations = source.GetType() != typeof(DbQuery<Order>);
        return source.Select(x => new OrderModel
        {
            OrderId = x.orderId,
            //example use ConvertToModel of some other repository
            BillingAddress = includeRelations ? AddressRepository.ConvertToModel(x.BillingAddress) : null,
            //example use another extension of some other repository
            Shipments = includeRelations && x.Shipments != null ? x.Shipments.SelectShipmentModel() : null
        });
    }
}

et ensuite dans votre GetOrderPage méthode:

    public IEnumerable<OrderModel> GetOrderPage(int page, int itemsPerPage, string searchString, string sortOrder, int? partnerId,
        out int totalCount)
    {
        IQueryable<Order> query = DbContext.Orders; //get queryable from db
        .....//do your filtering, sorting, paging (do not use .ToList() yet)

        return queryOrders.SelectOrderModel().AsEnumerable();
        //or, if you want to include relations
        return queryOrders.Include(x => x.BillingAddress).ToList().SelectOrderModel();
        //notice difference, first ToList(), then SelectOrderModel().
    }

laissez-moi vous expliquer:

la méthode statique ConvertToModel est accessible à tout autre dépôt, comme indiqué ci-dessus, j'utilise ConvertToModel à partir de AddressRepository .

la classe d'extension / méthode vous permet de convertir une entité pour un modèle. cela peut être IQueryable ou toute autre liste, collection .

maintenant voici la magie: si vous avez exécuté la requête avant d'appeler SelectOrderModel() extension, includeRelations à l'intérieur de l'extension sera vrai parce que le source N'est pas un type de requête de base de données (pas un linq-to-sql IQueryable ). Lorsque cela est vrai, l'extension peut appeler d'autres méthodes/extensions dans toute votre application pour convertir des modèles.

maintenant de l'autre côté: vous pouvez d'abord exécuter l'extension et ensuite continuer à faire du filtrage LINQ. le filtrage se produira dans la base de données éventuellement , parce que vous n'avez pas encore fait un .ToList() , l'extension n'est qu'une couche de traitement de vos requêtes. Linq-to-sql saura éventuellement quel filtrage appliquer dans la base de données. Le inlcudeRelations sera faux pour qu'il n'appelle pas d'autres méthodes c# que SQL ne comprend pas.

cela semble compliqué au début, les extensions peuvent être quelque chose de nouveau, mais c'est vraiment utile. Finalement, lorsque vous aurez configuré ceci pour tous les dépôts, un simple .Include() supplémentaire chargera les relations.

19
répondu CularBytes 2017-03-11 10:01:18

comme Chris l'a suggéré, la raison pour laquelle vous utilisez ViewModel ne vous empêche pas d'utiliser PagedList . Vous devez former une collection de vos objets ViewModel qui doit être envoyé à la vue pour pager plus.

voici un guide étape par étape sur la façon d'utiliser PagedList pour vos données viewmodel.

votre modèle de vue ( j'ai pris un exemple simple pour la brièveté et vous pouvez facilement le modifier pour s'adapter à vos besoins. )

public class QuestionViewModel
{
        public int QuestionId { get; set; }
        public string QuestionName { get; set; }
}

et la méthode D'Index de votre controller sera quelque chose comme

public ActionResult Index(int? page)
{
     var questions = new[] {
           new QuestionViewModel { QuestionId = 1, QuestionName = "Question 1" },
           new QuestionViewModel { QuestionId = 1, QuestionName = "Question 2" },
           new QuestionViewModel { QuestionId = 1, QuestionName = "Question 3" },
           new QuestionViewModel { QuestionId = 1, QuestionName = "Question 4" }
     };

     int pageSize = 3;
     int pageNumber = (page ?? 1);
     return View(questions.ToPagedList(pageNumber, pageSize));
}

et votre vue D'Index

@model PagedList.IPagedList<ViewModel.QuestionViewModel>
@using PagedList.Mvc; 
<link href="/Content/PagedList.css" rel="stylesheet" type="text/css" />


<table>

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.QuestionId)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.QuestionName)
        </td>
    </tr>
}

</table>

<br />

Page @(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of @Model.PageCount
@Html.PagedListPager( Model, page => Url.Action("Index", new { page }) )

voici le ainsi lien avec ma réponse qui a le guide étape par étape sur la façon dont vous pouvez utiliser PageList

14
répondu Dennis R 2017-05-23 12:26:27

j'ai modifié le code comme suit:"

ViewModel

using System.Collections.Generic;
using ContosoUniversity.Models;

namespace ContosoUniversity.ViewModels
{
    public class InstructorIndexData
    {
     public PagedList.IPagedList<Instructor> Instructors { get; set; }
     public PagedList.IPagedList<Course> Courses { get; set; }
     public PagedList.IPagedList<Enrollment> Enrollments { get; set; }
    }
}

contrôleur

public ActionResult Index(int? id, int? courseID,int? InstructorPage,int? CoursePage,int? EnrollmentPage)
{
 int instructPageNumber = (InstructorPage?? 1);
 int CoursePageNumber = (CoursePage?? 1);
 int EnrollmentPageNumber = (EnrollmentPage?? 1);
 var viewModel = new InstructorIndexData();
 viewModel.Instructors = db.Instructors
    .Include(i => i.OfficeAssignment)
    .Include(i => i.Courses.Select(c => c.Department))
    .OrderBy(i => i.LastName).ToPagedList(instructPageNumber,5);

 if (id != null)
 {
    ViewBag.InstructorID = id.Value;
    viewModel.Courses = viewModel.Instructors.Where(
        i => i.ID == id.Value).Single().Courses.ToPagedList(CoursePageNumber,5);
 }

 if (courseID != null)
 {
    ViewBag.CourseID = courseID.Value;
    viewModel.Enrollments = viewModel.Courses.Where(
        x => x.CourseID == courseID).Single().Enrollments.ToPagedList(EnrollmentPageNumber,5);
 }

 return View(viewModel);
}

Vue

<div>
   Page @(Model.Instructors.PageCount < Model.Instructors.PageNumber ? 0 : Model.Instructors.PageNumber) of @Model.Instructors.PageCount

   @Html.PagedListPager(Model.Instructors, page => Url.Action("Index", new {InstructorPage=page}))

</div>

j'espère que cela vous aidera!!

12
répondu ltedone 2016-08-19 14:54:01

j'ai trouvé comment faire. Je construisais une application très similaire à l'exemple/tutoriel dont vous avez parlé dans votre question originale.

voici un extrait du code qui a fonctionné pour moi:

        int pageSize = 4;
        int pageNumber = (page ?? 1);
        //Used the following two formulas so that it doesn't round down on the returned integer
        decimal totalPages = ((decimal)(viewModel.Teachers.Count() /(decimal) pageSize));     
        ViewBag.TotalPages = Math.Ceiling(totalPages);  
        //These next two functions could maybe be reduced to one function....would require some testing and building
        viewModel.Teachers = viewModel.Teachers.ToPagedList(pageNumber, pageSize);
        ViewBag.OnePageofTeachers = viewModel.Teachers;
        ViewBag.PageNumber = pageNumber;

        return View(viewModel);

j'ai ajouté

using.PagedList;

à mon contrôleur comme le tutoriel unis.

maintenant, à mon avis, Mon utilisation des instructions etc en haut, notez que je n'ai pas changé mon utilisation de l'instruction model.

@model CSHM.ViewModels.TeacherIndexData
@using PagedList;
@using PagedList.Mvc;
<link href="~/Content/PagedList.css" rel="stylesheet" type="text/css" />

puis en bas pour construire ma liste paginée, j'ai utilisé le suivant et il semble fonctionner. Je n'ai pas encore construit la fonctionnalité pour le sort courant, montrant les données connexes, filtres, etc mais je ne pense pas que ce sera difficile.

Page @ViewBag.PageNumber of @ViewBag.TotalPages

@Html.PagedListPager((IPagedList)ViewBag.OnePageofTeachers, page => Url.Action("Index", new { page }))

l'Espérance qui fonctionne pour vous. Laissez-moi savoir si cela fonctionne!!

2
répondu dave317 2015-02-09 19:49:11

le fait que vous utilisiez un modèle de vue n'a aucun rapport. La façon standard d'utiliser PagedList est de stocker" une page d'articles "comme une variable ViewBag . Tout ce que vous devez déterminer est ce que l'ensemble constitue ce que vous serez de pagination. Vous ne pouvez pas logiquement page multiples collections en même temps, donc en supposant que vous avez choisi Instructors :

ViewBag.OnePageOfItems = myViewModelInstance.Instructors.ToPagedList(pageNumber, 10);

alors, le reste du code standard fonctionne comme il l'a toujours fait.

0
répondu Chris Pratt 2014-08-04 18:48:44