Поделиться через


Часть 7. Добавление возможностей

Джо Стагнер

Tailspin Spyworks демонстрирует, насколько просто создавать мощные масштабируемые приложения для платформы .NET. В нем показано, как использовать новые функции ASP.NET 4 для создания интернет-магазина, включая покупки, оформления заказа и администрирования.

В этой серии руководств подробно описаны все шаги, предпринятые для создания примера приложения Tailspin Spyworks. Часть 7 добавляет дополнительные функции, такие как проверка учетных записей, обзоры продуктов и "популярные элементы" и "также приобретенные" пользовательские элементы управления.

Добавление возможностей

Хотя пользователи могут просматривать наш каталог, размещать товары в корзине и завершать процесс оформления заказа, существует ряд вспомогательных функций, которые мы включим для улучшения нашего сайта.

  1. Проверка учетных записей (перечисление размещенных заказов и просмотр сведений).
  2. Добавьте на интерфейсную страницу содержимое, определенное для контекста.
  3. Добавьте функцию, которая позволит пользователям просматривать продукты в каталоге.
  4. Создайте пользовательский элемент управления для отображения популярных элементов и размещения этого элемента управления на первой странице.
  5. Создайте пользовательский элемент управления "Также приобретено" и добавьте его на страницу сведений о продукте.
  6. Добавление страницы контактов.
  7. Добавьте страницу О программе.
  8. Глобальная ошибка

Проверка учетной записи

В папке Account создайте две ASPX-страницы с именем OrderList.aspx, а другую — OrderDetails.aspx.

OrderList.aspx будет использовать элементы управления GridView и EntityDataSource так же, как и ранее.

<div class="ContentHead">Order History</div><br />

<asp:GridView ID="GridView_OrderList" runat="server" AllowPaging="True" 
              ForeColor="#333333" GridLines="None" CellPadding="4" Width="100%" 
              AutoGenerateColumns="False" DataKeyNames="OrderID" 
              DataSourceID="EDS_Orders" AllowSorting="True" ViewStateMode="Disabled" >
  <AlternatingRowStyle BackColor="White" />
  <Columns>
    <asp:BoundField DataField="OrderID" HeaderText="OrderID" ReadOnly="True" 
                    SortExpression="OrderID" />
    <asp:BoundField DataField="CustomerName" HeaderText="Customer" 
                    SortExpression="CustomerName" />
    <asp:BoundField DataField="OrderDate" HeaderText="Order Date" 
                    SortExpression="OrderDate" />
    <asp:BoundField DataField="ShipDate" HeaderText="Ship Date" 
                    SortExpression="ShipDate" />
    <asp:HyperLinkField HeaderText="Show Details" Text="Show Details" 
                 DataNavigateUrlFields="OrderID" 
                 DataNavigateUrlFormatString="~/Account/OrderDetails.aspx?OrderID={0}" />
  </Columns>
  <FooterStyle BackColor="#990000" Font-Bold="True" ForeColor="White" />
  <HeaderStyle BackColor="#990000" Font-Bold="True" ForeColor="White" />
  <PagerStyle BackColor="#FFCC66" ForeColor="#333333" HorizontalAlign="Center" />
  <RowStyle BackColor="#FFFBD6" ForeColor="#333333" />
  <SelectedRowStyle BackColor="#FFCC66" Font-Bold="True" ForeColor="Navy" />
  <SortedAscendingCellStyle BackColor="#FDF5AC" />
  <SortedAscendingHeaderStyle BackColor="#4D0000" />
  <SortedDescendingCellStyle BackColor="#FCF6C0" />
  <SortedDescendingHeaderStyle BackColor="#820000" />
  <SortedAscendingCellStyle BackColor="#FDF5AC"></SortedAscendingCellStyle>
  <SortedAscendingHeaderStyle BackColor="#4D0000"></SortedAscendingHeaderStyle>
  <SortedDescendingCellStyle BackColor="#FCF6C0"></SortedDescendingCellStyle>
  <SortedDescendingHeaderStyle BackColor="#820000"></SortedDescendingHeaderStyle>
</asp:GridView>

<asp:EntityDataSource ID="EDS_Orders" runat="server" EnableFlattening="False" 
                      AutoGenerateWhereClause="True" 
                      Where="" 
                      OrderBy="it.OrderDate DESC"
                      ConnectionString="name=CommerceEntities"  
                      DefaultContainerName="CommerceEntities" 
                      EntitySetName="Orders" >
   <WhereParameters>
      <asp:SessionParameter Name="CustomerName" SessionField="UserName" />
   </WhereParameters>
</asp:EntityDataSource>

EntityDataSource выбирает записи из таблицы Orders, отфильтрованные по имени пользователя (см. Параметр WhereParameter), который мы задаем в переменной сеанса при входе пользователя.

Обратите внимание на следующие параметры в HyperlinkField gridView:

DataNavigateUrlFields="OrderID" DataNavigateUrlFormatString="~/Account/OrderDetails.aspx?OrderID={0}"

Они указывают ссылку на представление сведений о заказе для каждого продукта, указывая поле OrderID в качестве параметра QueryString на страницу OrderDetails.aspx.

OrderDetails.aspx

Мы будем использовать элемент управления EntityDataSource для доступа к Orders и FormView для отображения данных Order и другой EntityDataSource с GridView для отображения всех строк заказа.

<asp:FormView ID="FormView1" runat="server" CellPadding="4" 
                             DataKeyNames="OrderID" 
                             DataSourceID="EDS_Order" ForeColor="#333333" Width="250px">
   <FooterStyle BackColor="#990000" Font-Bold="True" ForeColor="White" />
   <HeaderStyle BackColor="#990000" Font-Bold="True" ForeColor="White" />
   <ItemTemplate>
      OrderID : <%# Eval("OrderID") %><br />
      CustomerName : <%# Eval("CustomerName") %><br />
      Order Date : <%# Eval("OrderDate") %><br />
      Ship Date : <%# Eval("ShipDate") %><br />
   </ItemTemplate>
   <PagerStyle BackColor="#FFCC66" ForeColor="#333333" HorizontalAlign="Center" />
   <RowStyle BackColor="#FFFBD6" ForeColor="#333333" />
</asp:FormView>
<asp:EntityDataSource ID="EDS_Order" runat="server"  EnableFlattening="False" 
                      ConnectionString="name=CommerceEntities" 
                      DefaultContainerName="CommerceEntities" 
                      EntitySetName="Orders" 
                      AutoGenerateWhereClause="True" 
                      Where="" 
                      EntityTypeFilter="" Select="">
   <WhereParameters>
      <asp:QueryStringParameter Name="OrderID" QueryStringField="OrderID" Type="Int32" />
   </WhereParameters>
</asp:EntityDataSource>

<asp:GridView ID="GridView_OrderDetails" runat="server" 
              AutoGenerateColumns="False" 
              DataKeyNames="ProductID,UnitCost,Quantity" 
              DataSourceID="EDS_OrderDetails" 
              CellPadding="4" GridLines="Vertical" CssClass="CartListItem" 
              onrowdatabound="MyList_RowDataBound" ShowFooter="True" 
              ViewStateMode="Disabled">
   <AlternatingRowStyle CssClass="CartListItemAlt" />
   <Columns>
     <asp:BoundField DataField="ProductID" HeaderText="Product ID" ReadOnly="True" 
                     SortExpression="ProductID"  />
     <asp:BoundField DataField="ModelNumber" HeaderText="Model Number"  
                     SortExpression="ModelNumber" />
     <asp:BoundField DataField="ModelName" HeaderText="Model Name" 
                     SortExpression="ModelName" />
     <asp:BoundField DataField="UnitCost" HeaderText="Unit Cost" ReadOnly="True" 
                     SortExpression="UnitCost" DataFormatString="{0:c}" />
     <asp:BoundField DataField="Quantity" HeaderText="Quantity" ReadOnly="True" 
                     SortExpression="Quantity" />
     <asp:TemplateField> 
       <HeaderTemplate>Item Total</HeaderTemplate>
       <ItemTemplate>
         <%# (Convert.ToDouble(Eval("Quantity")) *  Convert.ToDouble(Eval("UnitCost")))%>
       </ItemTemplate>
     </asp:TemplateField>
   </Columns>
   <FooterStyle CssClass="CartListFooter"/>
   <HeaderStyle  CssClass="CartListHead" />
 </asp:GridView> 
 <asp:EntityDataSource ID="EDS_OrderDetails" runat="server" 
                       ConnectionString="name=CommerceEntities" 
                       DefaultContainerName="CommerceEntities" 
                       EnableFlattening="False" 
                       EntitySetName="VewOrderDetails" 
                       AutoGenerateWhereClause="True" 
                       Where="">
   <WhereParameters>
     <asp:QueryStringParameter Name="OrderID" QueryStringField="OrderID" Type="Int32" />
   </WhereParameters>
</asp:EntityDataSource>

В файле Кода программной части (OrderDetails.aspx.cs) есть два маленьких фрагмента обслуживания.

Сначала необходимо убедиться, что OrderDetails всегда получает OrderId.

protected void Page_Load(object sender, EventArgs e)
{
  if (String.IsNullOrEmpty(Request.QueryString["OrderId"]))
     {
     Response.Redirect("~/Account/OrderList.aspx");
     }
}

Нам также нужно вычислить и отобразить итог заказа из строк.

decimal _CartTotal = 0;

protected void MyList_RowDataBound(object sender, GridViewRowEventArgs e)
{
  if (e.Row.RowType == DataControlRowType.DataRow)
     {
     TailspinSpyworks.Data_Access.VewOrderDetail myCart = new 
                                                        Data_Access.VewOrderDetail();
     myCart = (TailspinSpyworks.Data_Access.VewOrderDetail)e.Row.DataItem;
     _CartTotal += Convert.ToDecimal(myCart.UnitCost * myCart.Quantity);
     }
   else if (e.Row.RowType == DataControlRowType.Footer)
     {
     e.Row.Cells[5].Text = "Total: " + _CartTotal.ToString("C");
   }
}

Домашняя страница

Давайте добавим статическое содержимое на страницу Default.aspx.

Сначала создадим папку "Содержимое" и в ней папку "Изображения" (и включим изображение, которое будет использоваться на домашней странице).

В нижний заполнитель страницы Default.aspx добавьте следующую разметку.

<h2>
  <asp:LoginView ID="LoginView_VisitorGreeting" runat="server">
    <AnonymousTemplate>
       Welcome to the Store !
    </AnonymousTemplate>
    <LoggedInTemplate>
      Hi <asp:LoginName ID="LoginName_Welcome" runat="server" />. Thanks for coming back. 
    </LoggedInTemplate>
  </asp:LoginView>
</h2>

<p><strong>TailSpin Spyworks</strong> demonstrates how extraordinarily simple it is to create powerful, scalable applications for the .NET platform. </p>
<table>
  <tr>
    <td>               
      <h3>Some Implementation Features.</h3>
      <ul>
                <li><a href="#">CSS Based Design.</a></li>
                <li><a href="#">Data Access via Linq to Entities.</a></li>
                <li><a href="#">MasterPage driven design.</a></li>
                <li><a href="#">Modern ASP.NET Controls User.</a></li>
                <li><a href="#">Integrated Ajac Control Toolkit Editor.</a></li>
        </ul>
    </td>
    <td>
        <img src="Content/Images/SampleProductImage.gif" alt=""/>
    </td>
  </tr>
</table>
    
<table>
  <tr>
    <td colspan="2"><hr /></td>
  </tr>
  <tr>
    <td>               
        <!-- Popular Items -->
    </td>
    <td>  
      <center><h3>Ecommerce the .NET 4 Way</h3></center>
      <blockquote>
        <p>
        ASP.NET offers web developers the benefit of more that a decade of innovation. 
        This   demo leverages many of the latest features of ASP.NET development to     
        illustrate really simply building rich web applications with ASP.NET can be. 
        For more information about build web applications with ASP.NET please visit the 
        community web site at www.asp.net
        </p>
      </blockquote>
    </td>
  </tr>
</table>

<h3>Spyworks Event Calendar</h3>
<table>
  <tr class="rowH">
    <th>Date</th>
    <th>Title</th>
    <th>Description</th>
  </tr>
  <tr class="rowA">
    <td>June 01, 2011</td>
    <td>Sed vestibulum blandit</td>
    <td>
    Come and check out demos of all the newest Tailspin Spyworks products and experience 
    them hands on.
    </td>
  </tr>
  <tr class="rowB">
    <td>November 28, 2011</td>
    <td>Spyworks Product Demo</td>
    <td>
    Come and check out demos of all the newest Tailspin Spyworks products and experience 
    them hands on.
    </td>
  </tr>
  <tr class="rowA">
    <td>November 23, 2011</td>
    <td>Spyworks Product Demo</td>
    <td>
     Come and check out demos of all the newest Tailspin Spyworks products and experience 
     them hands on.
    </td>
  </tr>
  <tr class="rowB">
    <td>November 21, 2011</td>
    <td>Spyworks Product Demo</td>
    <td>
    Come and check out demos of all the newest Tailspin Spyworks products and experience 
    them hands on.
    </td>
  </tr>
</table>

Обзоры продуктов

Сначала мы добавим кнопку со ссылкой на форму, которую можно использовать для ввода обзора продукта.

<div class="SubContentHead">Reviews</div><br />
<a id="ReviewList_AddReview" href="ReviewAdd.aspx?productID=<%# Eval("ProductID") %>">
   <img id="Img2" runat="server" 
        src="~/Styles/Images/review_this_product.gif" alt="" />
</a>

Снимок экрана: расположение ссылки.

Обратите внимание, что мы передаем ProductID в строке запроса.

Далее добавим страницу с именем ReviewAdd.aspx.

На этой странице будет использоваться набор средств управления ASP.NET AJAX. Если вы еще не сделали этого, вы можете скачать его из DevExpress . Рекомендации по настройке набора средств для использования с Visual Studio см. здесь https://www.asp.net/learn/ajax-videos/video-76.aspx.

В режиме конструктора перетащите элементы управления и проверяющие элементы управления из панели элементов и создайте форму, как показано ниже.

Снимок экрана: форма.

Разметка будет выглядеть примерно так.

<asp:ToolkitScriptManager ID="ToolkitScriptManager1" runat="server">
</asp:ToolkitScriptManager>
<div class="ContentHead">Add Review - <asp:label id="ModelName" runat="server" /></div>
<div>
  <span class="NormalBold">Name</span><br />
  <asp:TextBox id="Name" runat="server" Width="400px" /><br />
  <asp:RequiredFieldValidator runat="server" id="RequiredFieldValidator1" 
                              ControlToValidate="Name" 
                              Display="Dynamic" 
                              CssClass="ValidationError" 
                              ErrorMessage="'Name' must not be left blank."  /><br />
  <span class="NormalBold">Email</span><br />
  <asp:TextBox id="Email" runat="server" Width="400px" /><br />
  <asp:RequiredFieldValidator runat="server" id="RequiredFieldValidator2" 
                              ControlToValidate="Email" Display="Dynamic" 
                              CssClass="ValidationError" 
                              ErrorMessage="'Email' must not be left blank." />
  <br /><hr /><br />
  <span class="NormalBold">Rating</span><br /><br />
  <asp:RadioButtonList ID="Rating" runat="server">
    <asp:ListItem value="5" selected="True" 
             Text='<img src="Styles/Images/reviewrating5.gif" alt=""> (Five Stars) '  />
    <asp:ListItem value="4" selected="True" 
             Text='<img src="Styles/Images/reviewrating4.gif" alt=""> (Four Stars) '  />
    <asp:ListItem value="3" selected="True" 
             Text='<img src="Styles/Images/reviewrating3.gif" alt=""> (Three Stars) '  />
    <asp:ListItem value="2" selected="True" 
             Text='<img src="Styles/Images/reviewrating2.gif" alt=""> (Two Stars) '  />
    <asp:ListItem value="1" selected="True" 
             Text='<img src="Styles/Images/reviewrating1.gif" alt=""> (One Stars) '  />
  </asp:RadioButtonList>
  <br /><hr /><br />
  <span class="NormalBold">Comments</span><br />
  <cc1:Editor ID="UserComment" runat="server" />
  <asp:RequiredFieldValidator runat="server" id="RequiredFieldValidator3" 
                              ControlToValidate="UserComment" Display="Dynamic" 
                              CssClass="ValidationError" 
                              ErrorMessage="Please enter your comment." /><br /><br />
  <asp:ImageButton ImageURL="Styles/Images/submit.gif" runat="server" 
                   id="ReviewAddBtn" onclick="ReviewAddBtn_Click" />
  <br /><br /><br />
</div>

Теперь, когда можно ввести отзывы, можно отобразить их на странице продукта.

Добавьте эту разметку на страницу ProductDetails.aspx.

<asp:ListView ID="ListView_Comments" runat="server" 
              DataKeyNames="ReviewID,ProductID,Rating" DataSourceID="EDS_CommentsList">
  <ItemTemplate>
    <tr>
      <td><%# Eval("CustomerName") %></td>
      <td>
        <img src='Styles/Images/ReviewRating_d<%# Eval("Rating") %>.gif' alt="">
        <br />
      </td>
      <td>
        <%# Eval("Comments") %>
      </td>
    </tr>
  </ItemTemplate>
  <AlternatingItemTemplate>
    <tr>
      <td><%# Eval("CustomerName") %></td>
      <td>
        <img src='Styles/Images/ReviewRating_da<%# Eval("Rating") %>.gif' alt="">
        <br />
      </td>
      <td><%# Eval("Comments") %></td>
    </tr>
  </AlternatingItemTemplate>
   <EmptyDataTemplate>
     <table runat="server">
       <tr><td>There are no reviews yet for this product.</td></tr>
     </table>
  </EmptyDataTemplate>
  <LayoutTemplate>
    <table runat="server">
      <tr runat="server">
        <td runat="server">
          <table ID="itemPlaceholderContainer" runat="server" border="1">
            <tr runat="server">
              <th runat="server">Customer</th>
              <th runat="server">Rating</th>
              <th runat="server">Comments</th>
             </tr>
             <tr ID="itemPlaceholder" runat="server"></tr>
           </table>
         </td>
       </tr>
       <tr runat="server">
         <td runat="server">
           <asp:DataPager ID="DataPager1" runat="server">
             <Fields>
               <asp:NextPreviousPagerField ButtonType="Button" 
                                           ShowFirstPageButton="True"
                                           ShowLastPageButton="True" />
             </Fields>
           </asp:DataPager>
         </td>
       </tr>
     </table>
  </LayoutTemplate>
</asp:ListView>
<asp:EntityDataSource ID="EDS_CommentsList" runat="server"  EnableFlattening="False" 
                       AutoGenerateWhereClause="True" 
                       EntityTypeFilter="" 
                       Select="" Where=""
                       ConnectionString="name=CommerceEntities" 
                       DefaultContainerName="CommerceEntities" 
                       EntitySetName="Reviews">
   <WhereParameters>
    <asp:QueryStringParameter Name="ProductID" QueryStringField="productID"  
                                               Type="Int32" />
  </WhereParameters>
</asp:EntityDataSource>

При запуске приложения и переходе к продукту отображаются сведения о продукте, включая отзывы клиентов.

Снимок экрана: отзывы клиентов.

Элемент управления популярными элементами (создание пользовательских элементов управления)

Чтобы увеличить продажи на вашем веб-сайте, мы добавим несколько функций для "предлагающей продажи" популярных или связанных продуктов.

Первой из этих функций будет список наиболее популярных продуктов в нашем каталоге продуктов.

Мы создадим "Пользовательский элемент управления" для отображения наиболее продаваемых товаров на домашней странице нашего приложения. Так как это будет элемент управления, его можно использовать на любой странице, просто перетащив элемент управления в конструкторе Visual Studio на любую страницу, которая нам нравится.

В обозревателе решений Visual Studio щелкните правой кнопкой мыши имя решения и создайте каталог с именем Controls. Хотя это необязательно, мы поможем организовать проект, создав все пользовательские элементы управления в каталоге Controls.

Щелкните правой кнопкой мыши папку controls и выберите "Создать элемент" :

Снимок экрана, на котором показано, где выбрать новый элемент.

Укажите имя для нашего элемента управления "PopularItems". Обратите внимание, что расширение файла для пользовательских элементов управления — ASCX, а не ASPX.

Наш пользовательский элемент управления "Популярные элементы" будет определен следующим образом.

<%@ OutputCache Duration="3600" VaryByParam="None" %>
<div class="MostPopularHead">Our most popular items this week</div>
<div id="PanelPopularItems" runat="server">
  <asp:Repeater ID="RepeaterItemsList" runat="server">
    <HeaderTemplate></HeaderTemplate>
      <ItemTemplate>               
        <a class='MostPopularItemText' 
           href='ProductDetails.aspx?productID=<%# Eval("ProductId") %>'>
                                               <%# Eval("ModelName") %></a><br />              
      </ItemTemplate>
    <FooterTemplate></FooterTemplate>
  </asp:Repeater>
</div>

Здесь мы используем метод, который мы еще не использовали в этом приложении. Мы используем элемент управления repeater и вместо элемента управления источником данных мы привязаем элемент управления Repeater к результатам LINQ to Entities запроса.

В коде программной части нашего элемента управления мы делаем это следующим образом.

using TailspinSpyworks.Data_Access;

protected void Page_Load(object sender, EventArgs e)
{
  using (CommerceEntities db = new CommerceEntities())
    {
    try
      {
      var query = (from ProductOrders in db.OrderDetails
                        join SelectedProducts in db.Products on ProductOrders.ProductID  
                        equals SelectedProducts.ProductID
                        group ProductOrders by new
                            {
                            ProductId = SelectedProducts.ProductID,
                            ModelName = SelectedProducts.ModelName
                            } into grp
                        select new
                            {
                            ModelName = grp.Key.ModelName,
                            ProductId = grp.Key.ProductId,
                            Quantity = grp.Sum(o => o.Quantity)
                            } into orderdgrp where orderdgrp.Quantity > 0 
                        orderby orderdgrp.Quantity descending select orderdgrp).Take(5);

                    RepeaterItemsList.DataSource = query;
                    RepeaterItemsList.DataBind(); 
      }
    catch (Exception exp)
      {
      throw new Exception("ERROR: Unable to Load Popular Items - " + 
                           exp.Message.ToString(), exp);
      }
    }
}

Обратите внимание на эту важную строку в верхней части разметки нашего элемента управления.

<%@ OutputCache Duration="3600" VaryByParam="None" %>

Так как самые популярные элементы не будут изменяться в минуту, можно добавить директиву aching, чтобы повысить производительность приложения. Эта директива приведет к тому, что код элементов управления будет выполняться только по истечении срока действия кэшированных выходных данных элемента управления. В противном случае будет использоваться кэшированная версия выходных данных элемента управления.

Теперь нам нужно только включить новый элемент управления на страницу Default.aspx.

Используйте перетаскивание, чтобы поместить экземпляр элемента управления в открытый столбец формы по умолчанию.

Снимок экрана: расположение экземпляра элемента управления.

Теперь при запуске приложения на домашней странице отображаются самые популярные элементы.

Снимок экрана: отображение наиболее популярных элементов на домашней странице.

Элемент управления "Также приобретено" (пользовательские элементы управления с параметрами)

Второй пользовательский элемент управления, который мы создадим, выведет наводящий продажи на следующий уровень, добавив специфичность контекста.

Логика вычисления наиболее часто приобретенных элементов нетривиальна.

Наш элемент управления "Также приобретено" выберет записи OrderDetails (ранее приобретенные) для выбранного productID и захватит идентификаторы OrderID для каждого найденного уникального заказа.

Затем мы выберем все продукты из всех этих заказов и суммируем количество приобретенных товаров. Мы отсортируем продукты по сумме этого количества и отобразим первые пять позиций.

Учитывая сложность этой логики, мы реализуем этот алгоритм как хранимую процедуру.

T-SQL для хранимой процедуры выглядит следующим образом.

ALTER PROCEDURE dbo.SelectPurchasedWithProducts
 @ProductID int
AS
        SELECT  TOP 5 
    OrderDetails.ProductID,
    Products.ModelName,
    SUM(OrderDetails.Quantity) as TotalNum

FROM    
    OrderDetails
  INNER JOIN Products ON OrderDetails.ProductID = Products.ProductID

WHERE   OrderID IN 
(
    /* This inner query should retrieve all orders that have contained the productID */
    SELECT DISTINCT OrderID 
    FROM OrderDetails
    WHERE ProductID = @ProductID
)
AND OrderDetails.ProductID != @ProductID 

GROUP BY OrderDetails.ProductID, Products.ModelName 

ORDER BY TotalNum DESC
RETURN

Обратите внимание, что эта хранимая процедура (SelectPurchasedWithProducts) существовала в базе данных, когда мы включили ее в наше приложение и когда мы создали модель данных сущности, мы указали, что в дополнение к необходимым таблицам и представлениям модель entityяной-процессов должна включать эту хранимую процедуру.

Чтобы получить доступ к хранимой процедуре из модели entity Data Model, необходимо импортировать функцию .

Дважды щелкните модель данных сущности в Обозреватель Решения, чтобы открыть ее в конструкторе и открыть обозреватель моделей, а затем щелкните правой кнопкой мыши конструктор и выберите "Добавить импорт функции".

Снимок экрана, на котором показано, где нажать кнопку Добавить импорт функции.

Это приведет к открытию этого диалогового окна.

Снимок экрана: открытое диалоговое окно.

Заполните поля, как показано выше, выбрав "SelectPurchasedWithProducts" и используйте имя процедуры для имени импортированной функции.

Нажмите кнопку "ОК".

После этого мы можем просто программируем хранимую процедуру так же, как и любой другой элемент в модели.

Поэтому в нашей папке Controls создайте пользовательский элемент управления с именем AlsoPurchased.ascx.

Разметка для этого элемента управления будет очень знакома элементу управления PopularItems.

<div>
<div class="MostPopularHead">
<asp:Label ID="LabelTitle" runat="server" Text=" Customers who bought this also bought:"></asp:Label></div>
<div id="PanelAlsoBoughtItems" runat="server">
    <asp:Repeater ID="RepeaterItemsList" runat="server">
       <HeaderTemplate></HeaderTemplate>
          <ItemTemplate>               
             <a class='MostPopularItemText' href='ProductDetails.aspx?productID=<%# Eval("ProductId") %>'><%# Eval("ModelName") %></a><br />              
          </ItemTemplate>
       <FooterTemplate></FooterTemplate>
    </asp:Repeater>
</div>
</div>

Заметное различие заключается в том, что не кэширование выходных данных, так как отрисовываемые элементы будут отличаться в разных продуктах.

ProductId будет "свойством" элемента управления.

private int _ProductId;

public int ProductId
{
get { return _ProductId ; }
set { _ProductId = Convert.ToInt32(value); }
}

В обработчике событий PreRender элемента управления мы сделали три действия.

  1. Убедитесь, что параметр ProductID задан.
  2. Узнайте, есть ли какие-либо продукты, которые были приобретены с текущим.
  3. Выведите некоторые элементы, как определено в No 2.

Обратите внимание, насколько легко вызывать хранимую процедуру через модель.

//--------------------------------------------------------------------------------------+
protected void Page_PreRender(object sender, EventArgs e)
{
  if (_ProductId < 1)
     {
     // This should never happen but we could expand the use of this control by reducing 
     // the dependency on the query string by selecting a few RANDOME products here. 
     Debug.Fail("ERROR : The Also Purchased Control Can not be used without 
                         setting the ProductId.");
     throw new Exception("ERROR : It is illegal to load the AlsoPurchased COntrol 
                                  without setting a ProductId.");
     }
      
  int ProductCount = 0;
  using (CommerceEntities db = new CommerceEntities())
    {
    try
      {
      var v = db.SelectPurchasedWithProducts(_ProductId);
      ProductCount = v.Count();
      }
    catch (Exception exp)
      {
      throw new Exception("ERROR: Unable to Retrieve Also Purchased Items - " + 
                                  exp.Message.ToString(), exp);
      }
    }

  if (ProductCount > 0)
     {
     WriteAlsoPurchased(_ProductId);              
     }
  else
     {
     WritePopularItems();
     }
}

Определив, что есть "также приобретены", можно просто привязать ретранслятор к результатам, возвращаемым запросом.

//-------------------------------------------------------------------------------------+
private void WriteAlsoPurchased(int currentProduct)
{
  using (CommerceEntities db = new CommerceEntities())
        {
        try
          {
          var v = db.SelectPurchasedWithProducts(currentProduct);
          RepeaterItemsList.DataSource = v;
          RepeaterItemsList.DataBind();
          }
         catch (Exception exp)
          {
          throw new Exception("ERROR: Unable to Write Also Purchased - " + 
                                                          exp.Message.ToString(), exp);
          }
        }
}

Если не было "также приобретенных" элементов, мы просто отобразим другие популярные элементы из нашего каталога.

//--------------------------------------------------------------------------------------+
private void WritePopularItems()
{
  using (CommerceEntities db = new CommerceEntities())
    {
    try
      {
      var query = (from ProductOrders in db.OrderDetails
                   join SelectedProducts in db.Products on ProductOrders.ProductID 
                   equals SelectedProducts.ProductID
                   group ProductOrders by new
                         {
                         ProductId = SelectedProducts.ProductID,
                         ModelName = SelectedProducts.ModelName
                         } into grp
                   select new
                         {
                         ModelName = grp.Key.ModelName,
                         ProductId = grp.Key.ProductId,
                         Quantity = grp.Sum(o => o.Quantity)
                         } into orderdgrp
                   where orderdgrp.Quantity > 0
                   orderby orderdgrp.Quantity descending
                   select orderdgrp).Take(5);
                   
      LabelTitle.Text = "Other items you might be interested in: ";
      RepeaterItemsList.DataSource = query;
      RepeaterItemsList.DataBind();
      }
    catch (Exception exp)
      {
      throw new Exception("ERROR: Unable to Load Popular Items - " + 
                                                        exp.Message.ToString(), exp);
      }
    }
}

Чтобы просмотреть элементы "Также приобретенные", откройте страницу ProductDetails.aspx и перетащите элемент управления AlsoPurchased из Обозреватель Решения, чтобы он отображался в этой позиции в разметке.

<table  border="0">
  <tr>
     <td>
       <img src='Catalog/Images/<%# Eval("ProductImage") %>'  border="0" 
                                                   alt='<%# Eval("ModelName") %>' />
     </td>
     <td><%# Eval("Description") %><br /><br /><br />  
         <uc1:AlsoPurchased ID="AlsoPurchased1" runat="server" />                 
     </td>
   </tr>
</table>

Это приведет к созданию ссылки на элемент управления в верхней части страницы ProductDetails.

<%@ Register src="Controls/AlsoPurchased.ascx" tagname="AlsoPurchased" tagprefix="uc1" %>

Так как для пользовательского элемента управления AlsoPurchased требуется номер ProductId, мы зададим свойство ProductID нашего элемента управления с помощью инструкции Eval для текущего элемента модели данных страницы.

Снимок экрана, на котором выделен идентификатор продукта.

При сборке и запуске и переходе к продукту отображаются элементы "Также приобретено".

Снимок экрана: элементы, которые также были приобретены.