- Огляд метод INDEX () дію Index () Action Method Recap
- Основні відомості про IQueryable <T> Understanding IQueryable <T>
- Додавання значення «сторінка» в URL-адресу Adding a "page" value to the URL
- Використання значення рядка запиту Using a Querystring value
- За допомогою значення Embedded URL-адресу Using Embedded URL values
- Наступний крок Next Step
Наша команда-партнер Artmisto
по Microsoft by Microsoft
Завантажити PDF-файл Download PDF
Це безкоштовна етап 8 керівництво із користування послугою «NerdDinner» , Покрокове розгляд як створити невеликий, але завершити, веб-додатки за допомогою ASP.NET MVC 1. This is step 8 of a free "NerdDinner" application tutorial that walks-through how to build a small, but complete, web application using ASP.NET MVC 1.
Крок 8 показано, як додати підтримку розбиття по сторінках наших / Dinners URL-адресу, щоб замість відображення тисячі вечерь за один раз, ми тільки раз - 10 майбутніх вечерь відображаються і надання кінцевим користувачам можливості сторінці назад і вперед по по всьому списку, зрозумілим чином SEO. Step 8 shows how to add paging support to our / Dinners URL so that instead of displaying 1000s of dinners at once, we'll only display 10 upcoming dinners at a time - and allow end-users to page back and forward through the entire list in an SEO friendly way.
Якщо ви використовуєте ASP.NET MVC 3, рекомендується слідувати Приступаючи до роботи з MVC 3 або MVC Music Store підручники. If you are using ASP.NET MVC 3, we recommend you follow the Getting Started With MVC 3 or MVC Music Store tutorials.
При успішному виконанні наш сайт буде тисячі майбутніх вечерь. If our site is successful, it will have thousands of upcoming dinners. Нам необхідно переконатися, що масштабується для задоволення всіх цих вечерь призначеному для користувача Інтерфейсу і дозволяє користувачам переглядати їх. We need to make sure that our UI scales to handle all of these dinners, and allows users to browse them. Для цього ми додамо підтримку розбиття по сторінках наших / Dinners URL-адресу так, щоб замість відображення тисячі вечерь на один раз, ми буде тільки одночасно - відобразити 10 вечерь майбутніх і надання кінцевим користувачам можливості сторінці назад і вперед по весь список в SEO зрозумілим способом . To enable this, we'll add paging support to our / Dinners URL so that instead of displaying 1000s of dinners at once , we'll only display 10 upcoming dinners at a time - and allow end-users to page back and forward through the entire list in an SEO friendly way.
Огляд метод INDEX () дію Index () Action Method Recap
Метод дії Index () всередині наш клас DinnersController в даний момент виглядає як нижче: The Index () action method within our DinnersController class currently looks like below:
// // GET: / Dinners / public ActionResult Index () {var dinners = dinnerRepository.FindUpcomingDinners (). ToList (); return View (dinners); }При запиті до / Dinners URL-адресу, він отримує список всіх майбутніх вечерь і виводить список всіх з них: When a request is made to the / Dinners URL, it retrieves a list of all upcoming dinners and then renders a listing of all of them out:
Основні відомості про IQueryable <T> Understanding IQueryable <T>
IQueryable <T> є інтерфейсом, яка була представлена як частина .NET 3.5 за допомогою LINQ. IQueryable <T> is an interface that was introduced with LINQ as part of .NET 3.5. Він підтримує потужні «відкладене виконання» сценарії, які ми можна скористатися перевагами для реалізації підтримки розбиття по сторінках. It enables powerful "deferred execution" scenarios that we can take advantage of to implement paging support.
У нашому DinnerRepository повертаєте IQueryable <вечерю> послідовності з нашого методу FindUpcomingDinners (): In our DinnerRepository we are returning an IQueryable <Dinner> sequence from our FindUpcomingDinners () method:
public class DinnerRepository {private NerdDinnerDataContext db = new NerdDinnerDataContext (); // // Query Methods public IQueryable <Dinner> FindUpcomingDinners () {return from dinner in db.Dinners where dinner.EventDate> DateTime.Now orderby dinner.EventDate select dinner; }IQueryable <вечерю> об'єкт, повернений наш метод FindUpcomingDinners () інкапсулює запит для отримання Dinner об'єкти з бази даних за допомогою LINQ to SQL. The IQueryable <Dinner> object returned by our FindUpcomingDinners () method encapsulates a query to retrieve Dinner objects from our database using LINQ to SQL. Що важливо не буде виконуватися запит до бази даних, поки робиться спроба доступу / ітерацію за даними в запиті або поки ми викликаємо метод ToList () на ньому. Importantly, it will not execute the query against the database until we attempt to access / iterate over the data in the query, or until we call the ToList () method on it. Код виклику нашого методу FindUpcomingDinners () при необхідності можуть додати додаткові «пов'язаний» operations / фільтри до IQueryable <вечерю> об'єкта перед виконанням запиту. The code calling our FindUpcomingDinners () method can optionally choose to add additional "chained" operations / filters to the IQueryable <Dinner> object before executing the query. LINQ to SQL, то вона потім виконання об'єднаний запит до бази даних, при запиті даних. LINQ to SQL is then smart enough to execute the combined query against the database when the data is requested.
Для реалізації логіки розбиття на сторінки, тобто застосовується до повертається IQueryable додаткових операторів «Skip» і «Take» дозволяє оновлювати метод дії Index () наших DinnersController <Dinner> послідовності перед викликом ToList () на ньому: To implement paging logic we can update our DinnersController's Index () action method so that it applies additional "Skip" and "Take" operators to the returned IQueryable <Dinner> sequence before calling ToList () on it:
// // GET: / Dinners / public ActionResult Index () {var upcomingDinners = dinnerRepository.FindUpcomingDinners (); var paginatedDinners = upcomingDinners.Skip (10) .Take (20) .ToList (); return View (paginatedDinners); }Наведений вище код пропускає перші 10 майбутніх вечері в базі даних і потім повертає 20 вечерь. The above code skips over the first 10 upcoming dinners in the database, and then returns back 20 dinners. LINQ to SQL здатний створювати оптимізованих запитів SQL, який виконує цю перевірку пропуск логіки в базі даних SQL - а не в веб сервера. LINQ to SQL is smart enough to construct an optimized SQL query that performs this skipping logic in the SQL database - and not in the web-server. Це означає, що навіть якщо у нас є мільйони майбутніх вечерь в базі даних, тільки 10, які потрібно буде вилучатись як частина цього запиту (зробивши його, ефективні і масштабовані). This means that even if we have millions of upcoming Dinners in the database, only the 10 we want will be retrieved as part of this request (making it efficient and scalable).
Додавання значення «сторінка» в URL-адресу Adding a "page" value to the URL
Замість жорстко запрограмованого певний діапазон сторінок, нам знадобляться наші URL-адреси для включають параметр «сторінка», який позначає діапазон які обід, запитує користувача. Instead of hard-coding a specific page range, we'll want our URLs to include a "page" parameter that indicates which Dinner range a user is requesting.
Використання значення рядка запиту Using a Querystring value
У наведеному нижче коді показано, як можна оновити метод дії наших Index () підтримує параметр рядка запиту і включення URL-адреси, наприклад / Dinners? сторінки = 2: The code below demonstrates how we can update our Index () action method to support a querystring parameter and enable URLs like / Dinners? page = 2:
// // GET: / Dinners / // / Dinners? Page = 2 public ActionResult Index (int? Page) {const int pageSize = 10; var upcomingDinners = dinnerRepository.FindUpcomingDinners (); var paginatedDinners = upcomingDinners.Skip ((page ?? 0) * pageSize) .Take (pageSize) .ToList (); return View (paginatedDinners); }Описаний вище метод Index () дію має параметр з ім'ям «сторінка». The Index () action method above has a parameter named "page". Параметр оголошений як значення типу обнуляє цілого (це які int? Вказує). The parameter is declared as a nullable integer (that is what int? Indicates). Це означає, що / Dinners? сторінки = 2 URL-адресу буде привести значення «2» не можна передати як значення параметра. This means that the / Dinners? Page = 2 URL will cause a value of "2" to be passed as the parameter value. / Dinners URL-адресу (без значення рядка запиту) призведе до передати значення null. The / Dinners URL (without a querystring value) will cause a null value to be passed.
Ми множення значення сторінки на розмір сторінки (в даному випадку 10 рядків), щоб визначити, скільки вечерь для пропуску. We are multiplying the page value by the page size (in this case 10 rows) to determine how many dinners to skip over. Ми використовуємо C # null «оператор об'єднання зі значенням» (?) що зручно при роботі з обнуляти типи. We are using the C # null "coalescing" operator (??) which is useful when dealing with nullable types. Наведений вище код призначає сторінки значення 0, якщо параметр сторінки має значення null. The code above assigns page the value of 0 if the page parameter is null.
За допомогою значення Embedded URL-адресу Using Embedded URL values
Альтернативою використанню значення рядка запиту буде впровадити параметра сторінки в фактичний URL-адресу, сам. An alternative to using a querystring value would be to embed the page parameter within the actual URL itself. Наприклад: / Dinners / Page / 2 або / вечерь / 2. For example: / Dinners / Page / 2 or / Dinners / 2. ASP.NET MVC включає в себе потужні механізм маршрутизації URL-адресу, який спрощує процес для підтримки таких сценаріїв, як це. ASP.NET MVC includes a powerful URL routing engine that makes it easy to support scenarios like this.
Зареєструвати настроюються правила маршрутизації, зіставлені з будь-якого вхідного URL-адресу або URL-адресу формат для будь-якого контролера класу або методом дії, нам потрібно. We can register custom routing rules that map any incoming URL or URL format to any controller class or action method we want. Всі ми повинні задача - відкрити файл Global.asax в наш проект: All we need to-do is to open the Global.asax file within our project:
А потім зареєструвати нове правило зіставлення, використовуючи допоміжний метод MapRoute () як перший виклик маршрути. MapRoute () нижче: And then register a new mapping rule using the MapRoute () helper method like the first call to routes.MapRoute () below:
public void RegisterRoutes (RouteCollection routes) {routes.IgnoreRoute ( "{resource} .axd / {* pathInfo}"); routes.MapRoute ( "UpcomingDinners", // Route name "Dinners / Page / {page}", // URL with params new {controller = "Dinners", action = "Index"} // Param defaults); routes.MapRoute ( "Default", // Route name "{controller} / {action} / {id}", // URL with params new {controller = "Home", action = "Index", id = ""} // Param defaults); } Void Application_Start () {RegisterRoutes (RouteTable.Routes); }Вище ми реєструємо нове правило маршрутизації з ім'ям «UpcomingDinners». Above we are registering a new routing rule named "UpcomingDinners". Ми, що вказує, він має наступний формат URL-адресу «вечерь / сторінки / {сторінці}» - де {page} є значення параметра, впроваджений в URL-адресу. We are indicating it has the URL format "Dinners / Page / {page}" - where {page} is a parameter value embedded within the URL. Третій параметр методу MapRoute () вказує, ми повинні були зіставлені URL-адреси, відповідає цьому формату, метод дії Index () DinnersController класу. The third parameter to the MapRoute () method indicates that we should map URLs that match this format to the Index () action method on the DinnersController class.
Можна використовувати точно Index () код, який ми виконували раніше в нашому рядки запиту - за винятком того, тепер наші параметр «сторінка» буде отримано з URL-адресу, а не рядок запиту: We can use the exact same Index () code we had before with our querystring scenario - except now our "page" parameter will come from the URL and not the querystring:
// // GET: / Dinners / // / Dinners / Page / 2 public ActionResult Index (int? Page) {const int pageSize = 10; var upcomingDinners = dinnerRepository.FindUpcomingDinners (); var paginatedDinners = upcomingDinners.Skip ((page ?? 0) * pageSize) .Take (pageSize) .ToList (); return View (paginatedDinners); }А тепер коли ми запустіть програму та ввести в / Dinners ми побачимо, перші 10 майбутніх вечері: And now when we run the application and type in / Dinners we'll see the first 10 upcoming dinners:
І якщо тип в / Dinners / Page / 1 буде показано на наступній сторінці вечерь: And when we type in / Dinners / Page / 1 we'll see the next page of dinners:
Останній крок на завершення нашого випадку розбиття по сторінках буде реалізувати «Далі» і «раніше» Навігаційний призначений для користувача інтерфейс в наш шаблон уявлення, щоб користувачі могли легко пропустити дані компанії Dinner. The last step to complete our paging scenario will be to implement "next" and "previous" navigation UI within our view template to enable users to easily skip over the Dinner data.
Щоб реалізувати це правильно, необхідно знати загальне число вечерь в базі даних, також як багато сторінок даних, це перетворюється в. To implement this correctly, we'll need to know the total number of Dinners in the database, as well as how many pages of data this translates to. Потім необхідно обчислити, чи є значення поточної запитаної «page» на початку або кінці даних також приховування і відображення для користувача інтерфейсу «раніше» і «далі» відповідним чином. We'll then need to calculate whether the currently requested "page" value is at the beginning or end of the data, and show or hide the "previous" and "next" UI accordingly. Ми може реалізовувати цю логіку всередині нашого методу дії Index (). We could implement this logic within our Index () action method. Крім того можна додати допоміжний клас для нашого проекту, який інкапсулює цю логіку повторно зручним чином. Alternatively we can add a helper class to our project that encapsulates this logic in a more re-usable way.
Нижче наведено простий «PaginatedList» допоміжний клас, який є похідним від списку <T> клас колекції вбудовані в .NET Framework. Below is a simple "PaginatedList" helper class that derives from the List <T> collection class built-into the .NET Framework. Вона реалізує клас колекції доступні для повторного використання, можна використовувати для розбиття по сторінках, будь-яка послідовність даних IQueryable. It implements a re-usable collection class that can be used to paginate any sequence of IQueryable data. У нашому додатку NerdDinner ми будемо змусити його працювати над IQueryable <вечерю> результати, але він може легко використовуватися проти IQueryable <продукту> або IQueryable <клієнта> призводить інших сценаріїв додатків: In our NerdDinner application we'll have it work over IQueryable <Dinner > results, but it could just as easily be used against IQueryable <Product> or IQueryable <Customer> results in other application scenarios:
public class PaginatedList <T>: List <T> {public int PageIndex {get; private set; } Public int PageSize {get; private set; } Public int TotalCount {get; private set; } Public int TotalPages {get; private set; } Public PaginatedList (IQueryable <T> source, int pageIndex, int pageSize) {PageIndex = pageIndex; PageSize = pageSize; TotalCount = source.Count (); TotalPages = (int) Math.Ceiling (TotalCount / (double) PageSize); this.AddRange (source.Skip (PageIndex * PageSize) .Take (PageSize)); } Public bool HasPreviousPage {get {return (PageIndex> 0); }} Public bool HasNextPage {get {return (PageIndex + 1 <TotalPages); }}}Зверніть увагу, яким чином обчислюється, і потім надає властивості, такі як «PageIndex», «PageSize», «TotalCount» і «TotalPages». Notice above how it calculates and then exposes properties like "PageIndex", "PageSize", "TotalCount", and "TotalPages". Він також надає дві допоміжні властивості «HasPreviousPage» і «HasNextPage», які вказують, чи є сторінка даних в колекції на початку або наприкінці початкової послідовності. It also then exposes two helper properties "HasPreviousPage" and "HasNextPage" that indicate whether the page of data in the collection is at the beginning or end of the original sequence. Наведений вище код викличе два SQL-запиту для запуску - перший для отримання кількості загальне число об'єктів Dinner (це не повертає об'єкти - замість цього він виконує інструкцію «SELECT COUNT», яка повертає ціле число), а друга - для отримання рядків з дані, ми повинні з бази даних для поточної сторінки даних. The above code will cause two SQL queries to be run - the first to retrieve the count of the total number of Dinner objects (this does not return the objects - rather it performs a "SELECT COUNT" statement that returns an integer), and the second to retrieve just the rows of data we need from our database for the current page of data.
Потім можна оновлювати наші DinnersController.Index () допоміжний метод для створення PaginatedList <Dinner> з наших DinnerRepository.FindUpcomingDinners () привести і передайте його в наш шаблон уявлення: We can then update our DinnersController.Index () helper method to create a PaginatedList < Dinner> from our DinnerRepository.FindUpcomingDinners () result, and pass it to our view template:
// // GET: / Dinners / // / Dinners / Page / 2 public ActionResult Index (int? Page) {const int pageSize = 10; var upcomingDinners = dinnerRepository.FindUpcomingDinners (); var paginatedDinners = new PaginatedList <Dinner> (upcomingDinners, page ?? 0, pageSize); return View (paginatedDinners); }Ми можемо потім оновити шаблон уявлення \ Views \ Dinners \ Index.aspx спадкування ViewPage <NerdDinner.Helpers.PaginatedList <вечерю>> замість ViewPage <IEnumerable <Вечеря >>, а потім додайте наступний код в нижню частину нашої шаблону подання для відображення або приховування наступного та попередньої Навігаційний призначений для користувача інтерфейс: We can then update the \ Views \ Dinners \ Index.aspx view template to inherit from ViewPage <NerdDinner.Helpers.PaginatedList <Dinner >> instead of ViewPage <IEnumerable <Dinner >>, and then add the following code to the bottom of our view-template to show or hide next and previous navigation UI:
<% If (Model.HasPreviousPage) {%> <% = Html.RouteLink ( "<<<", "UpcomingDinners", new {page = (Model.PageIndex-1)})%> <%}%> <% if (Model.HasNextPage) {%> <% = Html.RouteLink ( ">>>", "UpcomingDinners", new {page = (Model.PageIndex + 1)})%> <%}%>Зверніть увагу, як ми використовуємо Html.RouteLink () допоміжний метод для створення нашої гіперпосилань. Notice above how we are using the Html.RouteLink () helper method to generate our hyperlinks. Цей метод аналогічний Html.ActionLink () допоміжний метод, який ми використовували раніше. This method is similar to the Html.ActionLink () helper method we've used previously. Різниця в тому що створення URL-адресу, використовуючи правило маршрутизації, ми налаштовуємо в наш файл Global.asax «UpcomingDinners». The difference is that we are generating the URL using the "UpcomingDinners" routing rule we setup within our Global.asax file. Це гарантує, що нам достатньо створити URL-адреси до нашого методу дії Index (), мають формат: / Dinners / сторінки / {page}, за яким значення {сторінки} є змінною, ми надаємо вище залежно від поточного PageIndex. This ensures that we'll generate URLs to our Index () action method that have the format: / Dinners / Page / {page} - where the {page} value is a variable we are providing above based on the current PageIndex .
І тепер при виконанні додатка ще раз ми побачимо 10 вечерь одночасно в нашому засобі перегляду: And now when we run our application again we'll see 10 dinners at a time in our browser:
У нас також є <<<і>>> Навігаційний призначений для користувача інтерфейс в нижній частині сторінки, що дозволяє пропустити вперед і назад через наші дані за допомогою пошуку доступні ядра URL-адреси: We also have <<< and >>> navigation UI at the bottom of the page that allows us to skip forwards and backwards over our data using search engine accessible URLs:
Тема стороні: Основні відомості про наслідки IQueryable <T> Side Topic: Understanding the implications of IQueryable <T> IQueryable <T> - дуже потужна функція, яка включає різноманітні цікаві сценарії відкладеного виконання (наприклад розбиття по сторінках і композиції запити на основі). IQueryable <T> is a very powerful feature that enables a variety of interesting deferred execution scenarios (like paging and composition based queries). Всі ефективні можливості, ви повинні бути обережним з їх використання і переконайтеся, що її використовувати. As with all powerful features, you want to be careful with how you use it and make sure it is not abused. Дуже важливо для розпізнавання, повертаючи IQueryable <T> викликає код для додавання на оператор ланцюжка методів і тому брати участь у виконанні запиту ultimate дозволяє результатів зі сховищ. It is important to recognize that returning an IQueryable <T> result from your repository enables calling code to append on chained operator methods to it, and so participate in the ultimate query execution. Якщо ви хочете забезпечити цю можливість для викликає коду, то необхідно повернути назад IList <T> або IEnumerable <T> результати - містять результати запиту, який вже виконано. If you do not want to provide calling code this ability, then you should return back IList <T> or IEnumerable <T> results - which contain the results of a query that has already executed. Для сценаріїв розбиття на сторінки це зажадає Примусова відправка логіки розбиття на сторінки фактичні дані в репозиторій викликається методу. For pagination scenarios this would require you to push the actual data pagination logic into the repository method being called. У цьому сценарії ми може оновлювати наших FindUpcomingDinners () метод пошуку, щоб мати сигнатуру, що або повернуті PaginatedList: PaginatedList <Dinner> FindUpcomingDinners (int pageIndex, int pageSize) {} або поверненням розміщується IList <вечерю> і використовувати для отримання загального числа вечерь «totalCount» out param: IList <вечерю> FindUpcomingDinners (int pageIndex, int pageSize, out int totalCount) {} In this scenario we might update our FindUpcomingDinners () finder method to have a signature that either returned a PaginatedList: PaginatedList <Dinner> FindUpcomingDinners (int pageIndex, int pageSize) {} Or return back an IList <Dinner>, and use a "totalCount" out param to return the total count of Dinners: IList <Dinner> FindUpcomingDinners (int pageIndex, int pageSize, out int totalCount ) {}Наступний крок Next Step
Давайте тепер поглянемо на як можна додати підтримку перевірки автентичності та авторизації до нашого додатком. Let's now look at how we can add authentication and authorization support to our application.
Int?Page ?
Int?
Page ?
Int?