第 7 部分:加入功能

作者:Joe Stagner

Tailspin Spyworks 示範如何為 .NET 平臺建立強大且可調整的應用程式,非常簡單。 其中顯示如何使用 ASP.NET 4 中的絕佳新功能來建置線上商店,包括購物、結帳和管理。

本教學課程系列詳細說明建置 Tailspin Spyworks 範例應用程式所採取的所有步驟。 第 7 部分新增其他功能,例如帳戶檢閱、產品評論和「熱門專案」和「也購買」使用者控制項。

加入功能

雖然使用者可以流覽目錄、將專案放在購物車中,並完成結帳程式,但我們將會包含許多支援功能來改善網站。

  1. 帳戶檢閱 (下訂單並檢視詳細資料。)
  2. 將一些內容特定內容新增至首頁。
  3. 新增功能,讓使用者檢閱目錄中的產品。
  4. 建立使用者控制項以顯示熱門專案,並將該控制項放在首頁上。
  5. 建立「也購買」使用者控制項,並將其新增至產品詳細資料頁面。
  6. 新增連絡人頁面。
  7. 新增 [關於] 頁面。
  8. 全域錯誤

帳戶檢閱

在 [帳戶] 資料夾中,建立兩個名為 OrderList.aspx 的 .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 會從 UserName 篩選的 Orders 資料表中選取記錄, (查看使用者登入時,我們在會話變數中設定的 WhereParameter) 。

也請注意 GridView 的 HyperlinkField 中的這些參數:

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

這些會針對每個指定 OrderID 欄位做為 OrderDetails.aspx 頁面的 QueryString 參數的產品,指定 [訂單詳細資料] 檢視的連結。

OrderDetails.aspx

我們將使用 EntityDataSource 控制項來存取 Orders 和 FormView 來顯示 Order 資料,並使用 GridView 顯示另一個 EntityDataSource,以顯示所有 Order 的明細專案。

<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 頁面。

首先,我將建立一個「內容」資料夾,並在其中建立 Images 資料夾 (,並將包含要用於首頁的影像。)

在 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 Control 系結至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 頁面中加入新的控制項。

使用拖放,將控制項的實例放在預設表單的開啟資料行中。

顯示放置控制項實例位置的螢幕擷取畫面。

現在當我們執行應用程式時,首頁會顯示最受歡迎的專案。

顯示首頁如何顯示最熱門專案的螢幕擷取畫面。

具有參數) 的使用者控制項 (「也購買」控制項

我們將建立的第二個使用者控制項會藉由新增內容特定性,將建議銷售帶到下一個層級。

計算最上層「也已購買」專案的邏輯不簡單。

我們的 「也已購買」控制項會選取先前針對目前所選 ProductID 購買的 OrderDetails 記錄 () ,並針對找到的每個唯一訂單擷取 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) 存在於資料庫中,而當我們在應用程式中包含它時,以及當我們產生實體資料模型時,除了所需的資料表和檢視之外,實體資料模型也應該包含此預存程式。

若要從實體資料模型存取預存程式,我們需要匯入函式。

按兩下 [方案總管] 中的 [實體資料模型],在設計工具中開啟它,然後開啟 [模型瀏覽器],然後在設計工具中按一下滑鼠右鍵,然後選取 [新增函式匯入]。

顯示選取 [新增函式匯入位置] 的螢幕擷取畫面。

這麼做將會開啟此對話方塊。

顯示開啟對話方塊的螢幕擷取畫面。

填寫如上所示的欄位,選取 「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 會是控制項的 「property」。

private int _ProductId;

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

在控制項的 PreRender 事件處理常式中,我們想要執行三件事。

  1. 請確定已設定 ProductID。
  2. 查看是否有任何已隨目前產品一起購買的產品。
  3. 輸出 #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 編號,因此我們會針對頁面的目前資料模型專案使用 Eval 語句來設定控制項的 ProductID 屬性。

醒目提示產品識別碼的螢幕擷取畫面。

當我們建置並立即執行並流覽至產品時,我們會看到「也已購買」專案。

顯示已購買專案的螢幕擷取畫面。