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に結合し、結果をビューに表示する必要があります。
コードを次のように変更しました。
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>
これがお役に立てば幸いです!!
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がリレーションをロードします。
クリスが提案したように、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
の使用方法に関するステップバイステップガイドがあります
これを行う方法を見つけました。元の質問で説明した例/チュートリアルに非常によく似たアプリケーションを作成していました。
ここに私のために働いたコードのスニペットがあります:
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 }))
それがあなたのために働くことを願っています。うまくいったら教えてください!!
ビューモデルを使用しているという事実には意味がありません。 PagedList
を使用する標準的な方法は、「1ページのアイテム」をViewBag
変数として保存することです。あなたが決定しなければならないのは、どのコレクションがページングするものを構成するかです。同時に複数のコレクションを論理的にページングすることはできないため、Instructors
を選択したと仮定します。
ViewBag.OnePageOfItems = myViewModelInstance.Instructors.ToPagedList(pageNumber, 10);
その後、標準コードの残りの部分はいつものように機能します。