web-dev-qa-db-ja.com

ViewModel ASP.Net MVCでPagedListを使用する

ASP.NetアプリケーションでPagedListを使用しようとしていますが、MicrosoftのWebサイトでこの例を見つけました http://www.asp.net/mvc/tutorials/getting-started-with-ef-using -mvc/sorting-filtering-and-page-with-the-entity-framework-in-an-asp-net-mvc-application

ViewModelを使用する複雑な状況でPagedListを使用するにはどうすればよいですか?ここに投稿されたインストラクターの例に成功せずにPagedListを追加しようとしています: 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

問題は、ViewModelが単純なフィールドではなくクラスによって構成されているため、ToPageList()メソッドで結果を変換できないことです。

これはViewModel構造です:

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

3つのテーブルをViewModelに結合し、結果をビューに表示する必要があります。

25
ltedone

コードを次のように変更しました。

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

コントローラ

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

見る

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

これがお役に立てば幸いです!!

13
ltedone

ViewModelsを変更せずに、データベースからすべてのレコードをロードしないでそれを行おうとしている人のために。

リポジトリ

_    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.
    }
_

コントローラー

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

表示

_@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>
_

ボーナス

最初に上記を実行してから、おそらくこれを使用してください!

この質問は(ビュー)モデルに関するものなので、ページングに役立つだけでなく、エンティティを別々に保ちたい場合にアプリケーションの残りの部分に役立つ小さなソリューションを提供しますリポジトリ、およびアプリケーションの残りの部分にモデル(ビューモデルとして使用できる)を処理させます。

リポジトリ

注文リポジトリ(私の場合)で、モデルを変換する静的メソッドを追加します。

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

リポジトリクラスの下に、これを追加します。

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

そして、GetOrderPageメソッドで:

_    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().
    }
_

説明させてください:

静的ConvertToModelメソッドには、上記で使用した他のリポジトリからアクセスできます。私は、いくつかのConvertToModelからAddressRepositoryを使用します。

拡張クラス/メソッドを使用すると、エンティティをモデルに変換できます。 これは、IQueryableまたは他のリストであるcollectionです。

ここに魔法があります:SelectOrderModel()拡張を呼び出す前にクエリを実行した場合、includeRelationsはデータベースクエリタイプではないため、拡張内のsourceはtrueになります( linq-to-sqlではありませんIQueryable)。これが当てはまる場合、拡張機能は、モデルを変換するためにアプリケーション全体で他のメソッド/拡張機能を呼び出すことができます。

反対側では、最初に拡張機能を実行してから、LINQフィルタリングを続行できます。 フィルタリングは最終的にデータベースで行われます、まだ.ToList()を実行していないため、拡張機能はクエリを処理するレイヤーにすぎません。 Linq-to-sqlは、最終的にデータベースに適用するフィルタリングを認識します。 inlcudeRelationsはfalseであるため、SQLが理解できない他のc#メソッドを呼び出しません。

最初は複雑に見えますが、拡張機能は新しいものかもしれませんが、本当に便利です。最終的にすべてのリポジトリに対してこれを設定すると、単に.Include() extraがリレーションをロードします。

23
CularBytes

クリスが提案したように、ViewModelを使用している理由は、PagedListの使用を止めるものではありません。ページングのためにビューに送信する必要があるViewModelオブジェクトのコレクションを形成する必要があります。

ビューモデルデータにPagedListを使用する方法について、順を追って説明します。

ビューモデル(簡潔にするために簡単な例を取り上げましたが、ニーズに合わせて簡単に変更できます。

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

コントローラのIndexメソッドは次のようになります

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

インデックスビュー

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

これは SOリンク で、私の答えにはPageListの使用方法に関するステップバイステップガイドがあります

16
Dennis R

これを行う方法を見つけました。元の質問で説明した例/チュートリアルに非常によく似たアプリケーションを作成していました。

ここに私のために働いたコードのスニペットがあります:

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

追加した

using.PagedList;

チュートリアルが示すように、私のコントローラーに。

今、私の意見では、上部に使用ステートメントなどがあります。注:使用モデルのステートメントは変更していません。

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

そして、ページのリストを作成するために下部で次を使用しましたが、うまくいくようです。関連データ、フィルターなどを表示する現在のソートの機能はまだ組み込まれていませんが、それほど難しいとは思いません。

Page @ViewBag.PageNumber of @ViewBag.TotalPages

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

それがあなたのために働くことを願っています。うまくいったら教えてください!!

3
dave317

ビューモデルを使用しているという事実には意味がありません。 PagedListを使用する標準的な方法は、「1ページのアイテム」をViewBag変数として保存することです。あなたが決定しなければならないのは、どのコレクションがページングするものを構成するかです。同時に複数のコレクションを論理的にページングすることはできないため、Instructorsを選択したと仮定します。

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

その後、標準コードの残りの部分はいつものように機能します。

1
Chris Pratt