Вложенные веб-элементы управления данными (C#)Nested Data Web Controls (C#)

по Скотт Митчеллby Scott Mitchell

Скачивание примера приложения или Загрузка PDF-файлаDownload Sample App or Download PDF

В этом учебнике будет рассмотрено использование элемента Repeater, вложенного в другой элемент Repeater.In this tutorial we will explore how to use a Repeater nested inside another Repeater. В этих примерах показано, как заполнять внутренний элемент Repeater как декларативно, так и программно.The examples will illustrate how to populate the inner Repeater both declaratively and programmatically.

ВведениеIntroduction

Помимо статического синтаксиса HTML и привязки данных, шаблоны также могут включать веб-элементы управления и пользовательские элементы управления.In addition to static HTML and databinding syntax, templates can also include Web controls and User Controls. Эти веб-элементы управления могут быть назначены с помощью декларативного синтаксиса привязки данных, а также могут быть доступны программно в соответствующих обработчиках событий на стороне сервера.These Web controls can have their properties assigned via declarative, databinding syntax, or can be accessed programmatically in the appropriate server-side event handlers.

При внедрении элементов управления в шаблон внешний вид и взаимодействие с пользователем можно настраивать и улучшать.By embedding controls within a template, the appearance and user experience can be customized and improved upon. Например, в учебнике Использование полей TemplateField в руководстве по элементу управления GridView мы увидели, как настроить отображение GridView s, добавив элемент управления Calendar в TemplateField для отображения даты найма сотрудника. При добавлении элементов управления проверки в интерфейсы правки и вставки и настройке учебников по интерфейсу изменения данных мы увидели, как настраивать интерфейсы редактирования и вставки, добавляя элементы управления проверки, текстовые поля, элементов управления DropDownList и другие веб-элементы управления.For example, in the Using TemplateFields in the GridView Control tutorial, we saw how to customize the GridView s display by adding a Calendar control in a TemplateField to show an employee s hire date; in the Adding Validation Controls to the Editing and Inserting Interfaces and Customizing the Data Modification Interface tutorials, we saw how to customize the editing and inserting interfaces by adding validation controls, TextBoxes, DropDownLists, and other Web controls.

Шаблоны также могут содержать другие веб-элементы управления данными.Templates can also contain other data Web controls. Это значит, что у нас есть элемент управления DataList, который содержит другой DataList (или Repeater, GridView или DetailsView, и т. д.) в своих шаблонах.That is, we can have a DataList that contains another DataList (or Repeater or GridView or DetailsView, and so on) within its templates. Проблема такого интерфейса заключается в привязке соответствующих данных к веб-элементу управления внутренними данными.The challenge with such an interface is binding the appropriate data to the inner data Web control. Существует несколько различных подходов, в отличие от декларативных параметров, использующих ObjectDataSource для программных.There are a few different approaches available, ranging from declarative options using the ObjectDataSource to programmatic ones.

В этом учебнике будет рассмотрено использование элемента Repeater, вложенного в другой элемент Repeater.In this tutorial we will explore how to use a Repeater nested inside another Repeater. Внешний Repeater будет содержать элемент для каждой категории в базе данных, в котором отображается имя и описание категории s.The outer Repeater will contain an item for each category in the database, displaying the category s name and description. Внутренний Repeater каждого элемента категории отображает сведения для каждого продукта, принадлежащего этой категории (см. рис. 1) в маркированном списке.Each category item s inner Repeater will display information for each product belonging to that category (see Figure 1) in a bulleted list. В нашем примере показано, как заполнять внутренний элемент Repeater как декларативно, так и программно.Our examples will illustrate how to populate the inner Repeater both declaratively and programmatically.

каждую категорию вместе со своими продуктами, перечисленыEach Category, Along with its Products, are Listed

Рис. 1. перечисляются все категории вместе со своими продуктами (щелкните, чтобы просмотреть изображение с полным размером)Figure 1: Each Category, Along with its Products, are Listed (Click to view full-size image)

Шаг 1. Создание списка категорийStep 1: Creating the Category Listing

При создании страницы, использующей вложенные веб-элементы управления данными, полезно сначала разработать, создать и протестировать внешний веб-элемент управления данными, не беспокоясь о внутреннем вложенном элементе управления.When building a page that uses nested data Web controls, I find it helpful to design, create, and test the outermost data Web control first, without even worrying about the inner nested control. Таким образом, давайте начнем с прохода по шагам, необходимым для добавления элемента Repeater на страницу со списком имен и описаний для каждой категории.Therefore, let s start by walking through the steps necessary to add a Repeater to the page that lists the name and description for each category.

Сначала откройте страницу NestedControls.aspx в папке DataListRepeaterBasics и добавьте на страницу элемент управления Repeater, установив для свойства ID значение CategoryList.Start by opening the NestedControls.aspx page in the DataListRepeaterBasics folder and add a Repeater control to the page, setting its ID property to CategoryList. В смарт-теге Repeater s выберите Создание нового ObjectDataSource с именем CategoriesDataSource.From the Repeater s smart tag, choose to create a new ObjectDataSource named CategoriesDataSource.

назовите новый элемент ObjectDataSource CategoriesDataSourceName the New ObjectDataSource CategoriesDataSource

Рис. 2. Именование нового CategoriesDataSource ObjectDataSource (щелкните, чтобы просмотреть изображение с полным размером)Figure 2: Name the New ObjectDataSource CategoriesDataSource (Click to view full-size image)

Настройте ObjectDataSource таким образом, чтобы он запрашивает свои данные из GetCategories метода CategoriesBLL класса s.Configure the ObjectDataSource so that it pulls its data from the CategoriesBLL class s GetCategories method.

настроить ObjectDataSource для использования метода CategoriesBLL класса sConfigure the ObjectDataSource to Use the CategoriesBLL Class s GetCategories Method

Рис. 3. Настройка ObjectDataSource для использования метода GetCategories CategoriesBLL классов (щелкните, чтобы просмотреть изображение с полным размером)Figure 3: Configure the ObjectDataSource to Use the CategoriesBLL Class s GetCategories Method (Click to view full-size image)

Чтобы указать содержимое шаблона Repeater s, необходимо перейти к представлению исходного кода и вручную ввести декларативный синтаксис.To specify the Repeater s template content we need to go to the Source view and manually enter the declarative syntax. Добавьте ItemTemplate, отображающий имя категории s в элементе <h4>, и описание категории s в элементе абзаца (<p>).Add an ItemTemplate that displays the category s name in an <h4> element and the category s description in a paragraph element (<p>). Более того, давайте разделяйте каждую категорию горизонтальным правилом (<hr>).Furthermore, let s separate each category with a horizontal rule (<hr>). После внесения этих изменений страница должна содержать декларативный синтаксис для элемента управления Repeater и ObjectDataSource, аналогичный следующему:After making these changes your page should contain declarative syntax for the Repeater and ObjectDataSource that is similar to the following:

<asp:Repeater ID="CategoryList" DataSourceID="CategoriesDataSource"
    EnableViewState="False" runat="server">
    <ItemTemplate>
        <h4><%# Eval("CategoryName") %></h4>
        <p><%# Eval("Description") %></p>
    </ItemTemplate>
    <SeparatorTemplate>
        <hr />
    </SeparatorTemplate>
</asp:Repeater>
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetCategories" TypeName="CategoriesBLL">
</asp:ObjectDataSource>

На рис. 4 показан ход выполнения при просмотре в браузере.Figure 4 shows our progress when viewed through a browser.

перечислены имена и описания категорий s, разделенные горизонтальным правилом.Each Category s Name and Description is Listed, Separated by a Horizontal Rule

Рис. 4. в списке указаны имя и описание каждой категории, разделенные горизонтальным правилом (щелкните, чтобы просмотреть изображение с полным размером)Figure 4: Each Category s Name and Description is Listed, Separated by a Horizontal Rule (Click to view full-size image)

Шаг 2. Добавление вложенного повторителя продуктаStep 2: Adding the Nested Product Repeater

Теперь, когда список категорий завершен, нашей следующей задачей является добавление повторителя в ItemTemplate CategoryList s, который отображает сведения о продуктах, принадлежащих к соответствующей категории.With the category listing complete, our next task is to add a Repeater to the CategoryList s ItemTemplate that displays information about those products belonging to the appropriate category. Существует несколько способов получить данные для этого внутреннего элемента Repeater, два из которых мы рассмотрим чуть позже.There are a number of ways we can retrieve the data for this inner Repeater, two of which we'll explore shortly. Сейчас давайте просто создадим элемент Repeater для продуктов в ItemTemplate``CategoryList Repeater.For now, let s just create the products Repeater within the CategoryList Repeater s ItemTemplate. В частности, пусть для каждого продукта в маркированном списке отображается каждый продукт в виде списка, включая название и цену продукта.Specifically, let s have the product Repeater display each product in a bulleted list with each list item including the product s name and price.

Чтобы создать этот повторитель, необходимо вручную ввести декларативный синтаксис и шаблоны в ItemTemplate``CategoryList s.To create this Repeater we need to manually enter the inner Repeater s declarative syntax and templates into the CategoryList s ItemTemplate. Добавьте следующую разметку в CategoryList Repeater s ItemTemplate:Add the following markup within the CategoryList Repeater s ItemTemplate:

<asp:Repeater ID="ProductsByCategoryList" EnableViewState="False"
    runat="server">
    <HeaderTemplate>
        <ul>
    </HeaderTemplate>
    <ItemTemplate>
        <li><strong><%# Eval("ProductName") %></strong>
            (<%# Eval("UnitPrice", "{0:C}") %>)</li>
    </ItemTemplate>
    <FooterTemplate>
        </ul>
    </FooterTemplate>
</asp:Repeater>

Шаг 3. Привязка продуктов, относящихся к категории, к названием productsbycategorylist RepeaterStep 3: Binding the Category-Specific Products to the ProductsByCategoryList Repeater

Если на этом этапе вы посещаете страницу в браузере, экран будет выглядеть так же, как на рис. 4, так как мы еще не привязали данные к элементу Repeater.If you visit the page through a browser at this point, your screen will look the same as in Figure 4 because we ve yet to bind any data to the Repeater. Существует несколько способов получить соответствующие записи о продуктах и привязать их к повторителю, что более эффективно, чем другие.There are a few ways that we can grab the appropriate product records and bind them to the Repeater, some more efficient than others. Основной задачей является возврат соответствующих продуктов для указанной категории.The main challenge here is getting back the appropriate products for the specified category.

Доступ к данным для привязки к внутреннему элементу управления Repeater может осуществляться декларативно, с помощью ObjectDataSource в CategoryList Repeater s ItemTemplateили программно на странице кода программной части ASP.NET Page s.The data to bind to the inner Repeater control can either be accessed declaratively, through an ObjectDataSource in the CategoryList Repeater s ItemTemplate, or programmatically, from the ASP.NET page s code-behind page. Аналогичным образом эти данные могут быть привязаны к внутреннему элементу Repeater либо декларативно, либо с помощью декларативного свойства Repeater DataSourceID или программным путем, путем обращения к внутреннему элементу Repeater в обработчике событий CategoryList Repeater ItemDataBound, программной установки свойства DataSource и вызова его метода DataBind().Similarly, this data can be bound to the inner Repeater either declaratively - through the inner Repeater s DataSourceID property or through declarative databinding syntax or programmatically by referencing the inner Repeater in the CategoryList Repeater s ItemDataBound event handler, programmatically setting its DataSource property, and calling its DataBind() method. Давайте рассмотрим каждый из этих подходов.Let s explore each of these approaches.

Декларативный доступ к данным с помощью элемента управления ObjectDataSource и обработчика событийItemDataBoundAccessing the Data Declaratively with an ObjectDataSource Control and theItemDataBoundEvent Handler

Так как мы широко использовали элемент управления ObjectDataSource в этой серии руководств, самым естественным выбором для доступа к данным в этом примере является прикрепление к ObjectDataSource.Since we ve used the ObjectDataSource extensively throughout this tutorial series, the most natural choice for accessing data for this example is to stick with the ObjectDataSource. Класс ProductsBLL содержит метод GetProductsByCategoryID(categoryID), который возвращает сведения о продуктах, принадлежащих указанному categoryID .The ProductsBLL class has a GetProductsByCategoryID(categoryID) method that returns information about those products that belong to the specified categoryID. Таким образом, можно добавить ObjectDataSource в CategoryList Repeater ItemTemplate и настроить его для доступа к его данным из этого метода класса s.Therefore, we can add an ObjectDataSource to the CategoryList Repeater s ItemTemplate and configure it to access its data from this class s method.

К сожалению, элемент управления Repeater не позволяет редактировать его шаблоны с помощью представление конструирования, поэтому нам нужно добавить декларативный синтаксис для этого элемента.Unfortunately, the Repeater doesn t allow its templates to be edited through the Design view so we need to add the declarative syntax for this ObjectDataSource control by hand. Следующий синтаксис показывает CategoryList Repeater ItemTemplate после добавления этого нового элемента управления ObjectDataSource (ProductsByCategoryDataSource):The following syntax shows the CategoryList Repeater s ItemTemplate after adding this new ObjectDataSource (ProductsByCategoryDataSource):

<h4><%# Eval("CategoryName") %></h4>
<p><%# Eval("Description") %></p>
<asp:Repeater ID="ProductsByCategoryList" EnableViewState="False"
        DataSourceID="ProductsByCategoryDataSource" runat="server">
    <HeaderTemplate>
        <ul>
    </HeaderTemplate>
    <ItemTemplate>
        <li><strong><%# Eval("ProductName") %></strong> -
                sold as <%# Eval("QuantityPerUnit") %> at
                <%# Eval("UnitPrice", "{0:C}") %></li>
    </ItemTemplate>
    <FooterTemplate>
        </ul>
    </FooterTemplate>
</asp:Repeater>
<asp:ObjectDataSource ID="ProductsByCategoryDataSource" runat="server"
           SelectMethod="GetProductsByCategoryID" TypeName="ProductsBLL">
   <SelectParameters>
        <asp:Parameter Name="CategoryID" Type="Int32" />
   </SelectParameters>
</asp:ObjectDataSource>

При использовании подхода ObjectDataSource необходимо установить свойство ProductsByCategoryList Repeater s DataSourceID в ID элемента ObjectDataSource (ProductsByCategoryDataSource).When using the ObjectDataSource approach we need to set the ProductsByCategoryList Repeater s DataSourceID property to the ID of the ObjectDataSource (ProductsByCategoryDataSource). Кроме того, обратите внимание, что у элемента ObjectDataSource имеется элемент <asp:Parameter>, указывающий categoryID значение, которое будет передано в метод GetProductsByCategoryID(categoryID).Also, notice that our ObjectDataSource has an <asp:Parameter> element that specifies the categoryID value that will be passed into the GetProductsByCategoryID(categoryID) method. Но как указать это значение?But how do we specify this value? В идеале мы можем просто установить свойство DefaultValue элемента <asp:Parameter> с помощью синтаксиса DataBinding, например так:Ideally, we d be able to just set the DefaultValue property of the <asp:Parameter> element using databinding syntax, like so:

<asp:Parameter Name="CategoryID" Type="Int32"
     DefaultValue='<%# Eval("CategoryID")' />

К сожалению, синтаксис DataBinding допустим только в элементах управления, имеющих событие DataBinding.Unfortunately, databinding syntax is only valid in controls that have a DataBinding event. Класс Parameter не имеет такого события, поэтому приведенный выше синтаксис является недопустимым и приведет к ошибке во время выполнения.The Parameter class lacks such an event and therefore the above syntax is illegal and will result in a runtime error.

Чтобы задать это значение, необходимо создать обработчик событий для события CategoryList Repeater s ItemDataBound.To set this value, we need to create an event handler for the CategoryList Repeater s ItemDataBound event. Помните, что событие ItemDataBound срабатывает один раз для каждого элемента, привязанного к элементу Repeater.Recall that the ItemDataBound event fires once for each item bound to the Repeater. Таким образом, при каждом срабатывании этого события для внешнего элемента управления Repeater можно назначить текущее значение CategoryID ProductsByCategoryDataSource ObjectDataSource s CategoryID параметру.Therefore, each time this event fires for the outer Repeater we can assign the current CategoryID value to the ProductsByCategoryDataSource ObjectDataSource s CategoryID parameter.

Создайте обработчик событий для события CategoryList Repeater s ItemDataBound с помощью следующего кода:Create an event handler for the CategoryList Repeater s ItemDataBound event with the following code:

protected void CategoryList_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
    if (e.Item.ItemType == ListItemType.AlternatingItem ||
        e.Item.ItemType == ListItemType.Item)
    {
        // Reference the CategoriesRow object being bound to this RepeaterItem
        Northwind.CategoriesRow category =
            (Northwind.CategoriesRow)((System.Data.DataRowView)e.Item.DataItem).Row;
        // Reference the ProductsByCategoryDataSource ObjectDataSource
        ObjectDataSource ProductsByCategoryDataSource =
            (ObjectDataSource)e.Item.FindControl("ProductsByCategoryDataSource");
        // Set the CategoryID Parameter value
        ProductsByCategoryDataSource.SelectParameters["CategoryID"].DefaultValue =
            category.CategoryID.ToString();
    }
}

Этот обработчик событий начинает работу, гарантируя, что мы работаем над элементом данных, а не с верхним, нижним колонтитулом или разделителем.This event handler starts by ensuring that we re dealing with a data item rather than the header, footer, or separator item. Далее мы будем ссылаться на фактический экземпляр CategoriesRow, который только что был привязан к текущему RepeaterItem.Next, we reference the actual CategoriesRow instance that has just been bound to the current RepeaterItem. Наконец, мы будем ссылаться на элемент ObjectDataSource в ItemTemplate и присваиваем ему значение параметра CategoryID CategoryID текущего RepeaterItem.Finally, we reference the ObjectDataSource in the ItemTemplate and assign its CategoryID parameter value to the CategoryID of the current RepeaterItem.

С помощью этого обработчика событий ProductsByCategoryList Repeater в каждой RepeaterItem привязан к этим продуктам в категории RepeaterItem s.With this event handler, the ProductsByCategoryList Repeater in each RepeaterItem is bound to those products in the RepeaterItem s category. На рис. 5 показан снимок экрана с полученным результатом.Figure 5 shows a screen shot of the resulting output.

внешний элемент Repeater перечисляет каждую категорию; в внутреннем списке перечислены продукты для этой категории.The Outer Repeater Lists Each Category; the Inner One Lists the Products for that Category

Рис. 5. внешний элемент Repeater перечисляет каждую категорию; в внутреннем списке перечислены продукты для этой категории (щелкните, чтобы просмотреть изображение с полным размером).Figure 5: The Outer Repeater Lists Each Category; the Inner One Lists the Products for that Category (Click to view full-size image)

Программный доступ к продуктам по категории данныхAccessing the Products by Category Data Programmatically

Вместо того чтобы использовать ObjectDataSource для получения продуктов для текущей категории, можно создать метод в нашем классе кода программной части ASP.NET Page s (или в папке App_Code или в отдельном проекте библиотеки классов), который возвращает соответствующий набор продуктов при передаче в CategoryID.Instead of using an ObjectDataSource to retrieve the products for the current category, we could create a method in our ASP.NET page s code-behind class (or in the App_Code folder or in a separate Class Library project) that returns the appropriate set of products when passed in a CategoryID. Представьте, что в нашем классе кода программной части ASP.NET Page s есть такой метод, который был назван GetProductsInCategory(categoryID).Imagine that we had such a method in our ASP.NET page s code-behind class and that it was named GetProductsInCategory(categoryID). С помощью этого метода можно привязать продукты для текущей категории к внутреннему элементу Repeater, используя следующий декларативный синтаксис:With this method in place we could bind the products for the current category to the inner Repeater using the following declarative syntax:

<asp:Repeater runat="server" ID="ProductsByCategoryList" EnableViewState="False"
      DataSource='<%# GetProductsInCategory((int)(Eval("CategoryID"))) %>'>
  ...
</asp:Repeater>

Свойство Repeater s DataSource использует синтаксис привязки данных, чтобы указать, что его данные поступают из метода GetProductsInCategory(categoryID).The Repeater s DataSource property uses the databinding syntax to indicate that its data comes from the GetProductsInCategory(categoryID) method. Поскольку Eval("CategoryID") возвращает значение типа Object, мы передаем объект в Integer перед передачей его в метод GetProductsInCategory(categoryID).Since Eval("CategoryID") returns a value of type Object, we cast the object to an Integer before passing it into the GetProductsInCategory(categoryID) method. Обратите внимание, что CategoryID, к которому можно получить доступ с помощью синтаксиса DataBinding, — это CategoryID во внешнем элементе repeater (CategoryList), который привязан к записям в таблице Categories.Note that the CategoryID accessed here via the databinding syntax is the CategoryID in the outer Repeater (CategoryList), the one that s bound to the records in the Categories table. Поэтому мы понимаем, что CategoryID не может быть значением NULL базы данных, поэтому мы можем явно привести метод Eval, не проверяя, было ли мы переработаем с DBNull.Therefore, we know that CategoryID cannot be a database NULL value, which is why we can blindly cast the Eval method without checking if we re dealing with a DBNull.

При таком подходе необходимо создать метод GetProductsInCategory(categoryID) и получить соответствующий набор продуктов с учетом предоставленной categoryID .With this approach, we need to create the GetProductsInCategory(categoryID) method and have it retrieve the appropriate set of products given the supplied categoryID. Это можно сделать, просто возвращая ProductsDataTable, возвращенный методом ProductsBLL класса s GetProductsByCategoryID(categoryID).We can do this by simply returning the ProductsDataTable returned by the ProductsBLL class s GetProductsByCategoryID(categoryID) method. Давайте создадим метод GetProductsInCategory(categoryID) в классе кода программной части для нашей NestedControls.aspx страницы.Let s create the GetProductsInCategory(categoryID) method in the code-behind class for our NestedControls.aspx page. Для этого используйте следующий код:Do so using the following code:

protected Northwind.ProductsDataTable GetProductsInCategory(int categoryID)
{
    // Create an instance of the ProductsBLL class
    ProductsBLL productAPI = new ProductsBLL();
    // Return the products in the category
    return productAPI.GetProductsByCategoryID(categoryID);
}

Этот метод просто создает экземпляр метода ProductsBLL и возвращает результаты метода GetProductsByCategoryID(categoryID).This method simply creates an instance of the ProductsBLL method and returns the results of the GetProductsByCategoryID(categoryID) method. Обратите внимание, что метод должен быть помечен Public или Protected; Если метод помечен как Private, он будет недоступен из декларативной разметки ASP.NET Page s.Note that the method must be marked Public or Protected; if the method is marked Private, it will not be accessible from the ASP.NET page s declarative markup.

После внесения этих изменений для использования этой новой методики просмотрите страницу в браузере.After making these changes to use this new technique, take a moment to view the page through a browser. Выходные данные должны быть идентичны выходным данным при использовании обработчика событий ObjectDataSource и ItemDataBound (см. рис. 5 для просмотра снимка экрана).The output should be identical to the output when using the ObjectDataSource and ItemDataBound event handler approach (refer back to Figure 5 to see a screen shot).

Note

Может показаться, что бусиворк создает метод GetProductsInCategory(categoryID) в классе кода программной части ASP.NET Page s.It may seem like busywork to create the GetProductsInCategory(categoryID) method in the ASP.NET page s code-behind class. В конце концов, этот метод просто создает экземпляр класса ProductsBLL и возвращает результаты его GetProductsByCategoryID(categoryID) метода.After all, this method simply creates an instance of the ProductsBLL class and returns the results of its GetProductsByCategoryID(categoryID) method. Почему бы не просто вызывать этот метод непосредственно из синтаксиса привязки данных во внутреннем элементе Repeater, например: DataSource='<%# ProductsBLL.GetProductsByCategoryID((int)(Eval("CategoryID"))) %>'?Why not just call this method directly from the databinding syntax in the inner Repeater, like: DataSource='<%# ProductsBLL.GetProductsByCategoryID((int)(Eval("CategoryID"))) %>'? Несмотря на то, что этот синтаксис не работает с нашей текущей реализацией класса ProductsBLL (так как метод GetProductsByCategoryID(categoryID) является методом экземпляра), можно изменить ProductsBLL, включив в него статический метод GetProductsByCategoryID(categoryID) или включить в класс статический метод Instance() для возврата нового экземпляра класса ProductsBLL.Although this syntax won't work with our current implementation of the ProductsBLL class (since the GetProductsByCategoryID(categoryID) method is an instance method), you could modify ProductsBLL to include a static GetProductsByCategoryID(categoryID) method or have the class include a static Instance() method to return a new instance of the ProductsBLL class.

Хотя такие изменения устраняют необходимость в методе GetProductsInCategory(categoryID) в классе ASP.NET Page s, метод класса кода программной части обеспечивает большую гибкость при работе с полученными данными, как мы вскоре увидим.While such modifications would eliminate the need for the GetProductsInCategory(categoryID) method in the ASP.NET page s code-behind class, the code-behind class method gives us more flexibility in working with the data retrieved, as we'll see shortly.

Извлечение всех сведений о продукте за один разRetrieving All of the Product Information at Once

Два метода предыдущей, которые мы проверили, загрузили эти продукты для текущей категории, вызвав метод ProductsBLL класса GetProductsByCategoryID(categoryID) (первый подход сделал это с помощью элемента управления ObjectDataSource, второй через метод GetProductsInCategory(categoryID) в классе кода программной части).The two pervious techniques we ve examined grab those products for the current category by making a call to the ProductsBLL class s GetProductsByCategoryID(categoryID) method (the first approach did so through an ObjectDataSource, the second through the GetProductsInCategory(categoryID) method in the code-behind class). Каждый раз при вызове этого метода уровень бизнес-логики обращается к слою доступа к данным, который запрашивает базу данных с помощью инструкции SQL, возвращающей строки из таблицы Products, поле CategoryID которого соответствует указанному входному параметру.Each time this method is invoked, the Business Logic Layer calls down to the Data Access Layer, which queries the database with a SQL statement that returns rows from the Products table whose CategoryID field matches the supplied input parameter.

Учитывая n категорий в системе, этот подход приводит к порешению N + 1 запросов к базе данных для получения всех категорий, а затем n вызовов для получения продуктов, относящихся к каждой категории.Given N categories in the system, this approach nets N + 1 calls to the database one database query to get all of the categories and then N calls to get the products specific to each category. Однако мы можем получить все необходимые данные только в двух базах данных, вызвав один вызов для получения всех категорий, а другой — для получения всех продуктов.We can, however, retrieve all the needed data in just two database calls one call to get all of the categories and another to get all of the products. После получения всех продуктов можно отфильтровать эти продукты таким образом, чтобы только продукты, соответствующие текущему CategoryID, были привязаны к внутреннему элементу Repeater этой категории.Once we have all of the products, we can filter those products so that only the products matching the current CategoryID are bound to that category s inner Repeater.

Чтобы обеспечить эту функциональность, необходимо внести небольшое изменение в метод GetProductsInCategory(categoryID) в нашем классе кода программной части ASP.NET Page s.To provide this functionality, we only need to make a slight modification to the GetProductsInCategory(categoryID) method in our ASP.NET page s code-behind class. Вместо того, чтобы вслепую возврат результатов метода ProductsBLL класса GetProductsByCategoryID(categoryID), мы можем сначала получить доступ ко всем продуктам (если они еще не были доступны), а затем вернуть только отфильтрованное представление продуктов на основе переданного CategoryID.Rather than blindly returning the results of the ProductsBLL class s GetProductsByCategoryID(categoryID) method, we can instead first access all of the products (if they haven't been accessed already) and then return just the filtered view of the products based on the passed-in CategoryID.

private Northwind.ProductsDataTable allProducts = null;
protected Northwind.ProductsDataTable GetProductsInCategory(int categoryID)
{
    // First, see if we've yet to have accessed all of the product information
    if (allProducts == null)
    {
        ProductsBLL productAPI = new ProductsBLL();
        allProducts = productAPI.GetProducts();
    }
    // Return the filtered view
    allProducts.DefaultView.RowFilter = "CategoryID = " + categoryID;
    return allProducts;
}

Обратите внимание на добавление переменной уровня страницы allProducts.Note the addition of the page-level variable, allProducts. Он содержит сведения обо всех продуктах и заполняется при первом вызове метода GetProductsInCategory(categoryID).This holds information about all of the products and is populated the first time the GetProductsInCategory(categoryID) method is invoked. Убедившись, что объект allProducts создан и заполнен, метод фильтрует результаты DataTable, так что доступны только те строки, чьи CategoryID соответствуют заданным CategoryID.After ensuring that the allProducts object has been created and populated, the method filters the DataTable s results such that only those rows whose CategoryID matches the specified CategoryID are accessible. Такой подход сокращает количество обращений к базе данных с N + 1 до двух.This approach reduces the number of times the database is accessed from N + 1 down to two.

Это улучшение не вносит никаких изменений в отображаемую разметку страницы и не приводит к уменьшению числа записей, чем при использовании другого подхода.This enhancement does not introduce any change to the rendered markup of the page, nor does it bring back fewer records than the other approach. Он просто сокращает количество вызовов к базе данных.It simply reduces the number of calls to the database.

Note

Может быть интуитивно понятно, что уменьшение количества обращений к базе данных должно было бы повысить производительность.One might intuitively reason that reducing the number of database accesses would assuredly improve performance. Однако это может быть не так.However, this might not be the case. Например, если имеется большое количество продуктов, для которых CategoryID NULL, то вызов метода GetProducts возвращает ряд продуктов, которые никогда не отображаются.If you have a large number of products whose CategoryID is NULL, for example, then the call to the GetProducts method returns a number of products that are never displayed. Кроме того, возврат всех продуктов может быть непроизводительна, если вы восстановите подмножество категорий, что может быть, если вы реализовали подкачку.Moreover, returning all of the products can be wasteful if you re only showing a subset of the categories, which might be the case if you have implemented paging.

Как всегда, когда дело доходит до анализа производительности двух методов, единственной мерой завоюют является выполнение управляемых тестов, предназначенных для распространенных сценариев работы приложения.As always, when it comes to analyzing the performance of two techniques, the only surefire measure is to run controlled tests tailored for your application s common case scenarios.

СводкаSummary

В этом учебнике мы увидели, как вложить один веб-элемент управления данными в другой, и, в частности, исследовать, как внешний Repeater отображает элемент для каждой категории с внутренним элементом управления Repeater, в котором перечислены продукты для каждой категории в маркированном списке.In this tutorial we saw how to nest one data Web control within another, specifically examining how to have an outer Repeater display an item for each category with an inner Repeater listing the products for each category in a bulleted list. Основная задача при создании вложенного пользовательского интерфейса заключается в доступе и привязке правильных данных к веб-элементу управления внутренними данными.The main challenge in building a nested user interface lies in accessing and binding the correct data to the inner data Web control. Доступны различные методы, два из которых мы рассматривали в этом учебнике.There are a variety of techniques available, two of which we examined in this tutorial. Первый способ рассматривал использование элемента управления ObjectDataSource во внешних ItemTemplateах веб-элементов управления данными, которые были привязаны к веб-элементу управление внутренними данными с помощью его свойства DataSourceID.The first approach examined used an ObjectDataSource in the outer data Web control s ItemTemplate that was bound to the inner data Web control through its DataSourceID property. Второй метод обращается к данным через метод в классе кода программной части ASP.NET Page s.The second technique accessed the data via a method in the ASP.NET page s code-behind class. Затем этот метод можно привязать к свойству DataSource внутреннего веб-элемента управления данными с помощью синтаксиса DataBinding.This method can then be bound to the inner data Web control s DataSource property through databinding syntax.

В то время как вложенный пользовательский интерфейс, рассмотренный в этом учебнике, использовал элемент управления Repeater, вложенный в Repeater, эти методы можно расширить на другие веб-элементы.While the nested user interface examined in this tutorial used a Repeater nested within a Repeater, these techniques can be extended to the other data Web controls. Можно вложить элемент управления Repeater в элемент управления GridView или GridView внутри DataList и т. д.You can nest a Repeater within a GridView, or a GridView within a DataList, and so on.

Поздравляем с программированием!Happy Programming!

Об автореAbout the Author

Скотт Митчелл, автор семи книг по ASP/ASP. NET и основатель 4GuysFromRolla.com, работал с веб-технологиями Майкрософт с 1998.Scott Mitchell, author of seven ASP/ASP.NET books and founder of 4GuysFromRolla.com, has been working with Microsoft Web technologies since 1998. Скотт работает как независимый консультант, преподаватель и модуль записи.Scott works as an independent consultant, trainer, and writer. Его последняя книга — Sams обучать себя ASP.NET 2,0 за 24 часа.His latest book is Sams Teach Yourself ASP.NET 2.0 in 24 Hours. Он доступен по адресу mitchell@4GuysFromRolla.com.He can be reached at mitchell@4GuysFromRolla.com. или через его блог, который можно найти по адресу http://ScottOnWriting.NET.or via his blog, which can be found at http://ScottOnWriting.NET.

Специальная благодарностьSpecial Thanks To

Эта серия руководств была рассмотрена многими полезными рецензентами.This tutorial series was reviewed by many helpful reviewers. Потенциальным рецензентам для этого учебника были Зак Jones и основными рецензентами.Lead reviewers for this tutorial were Zack Jones and Liz Shulok. Хотите ознакомиться с моими будущими статьями MSDN?Interested in reviewing my upcoming MSDN articles? Если это так, расположите строку в mitchell@4GuysFromRolla.com.If so, drop me a line at mitchell@4GuysFromRolla.com.