Parte 7: Aggiunta di funzionalità

di Joe Stagner

Tailspin Spyworks dimostra quanto sia straordinariamente semplice creare applicazioni potenti e scalabili per la piattaforma .NET. Mostra come usare le nuove funzionalità di ASP.NET 4 per creare un negozio online, tra cui shopping, checkout e amministrazione.

Questa serie di esercitazioni illustra in dettaglio tutti i passaggi eseguiti per compilare l'applicazione di esempio Tailspin Spyworks. La parte 7 aggiunge funzionalità aggiuntive, ad esempio la revisione dell'account, le recensioni dei prodotti e i controlli utente "popolari" e "acquistati".

Aggiunta di funzionalità

Anche se gli utenti possono esplorare il catalogo, posizionare gli elementi nel carrello degli acquisti e completare il processo di pagamento, ci sono numerose funzionalità di supporto che verranno incluse per migliorare il nostro sito.

  1. Revisione dell'account (Elenchi ordini inseriti e visualizza i dettagli).
  2. Aggiungere un contenuto specifico del contesto alla pagina iniziale.
  3. Aggiungere una funzionalità per consentire agli utenti di esaminare i prodotti nel catalogo.
  4. Creare un controllo utente per visualizzare elementi comuni e posizionare il controllo nella pagina iniziale.
  5. Creare un controllo utente "Anche acquistato" e aggiungerlo alla pagina dei dettagli del prodotto.
  6. Aggiungere una pagina contatto.
  7. Aggiungere una pagina Informazioni.
  8. Errore globale

Revisione account

Nella cartella "Account" creare due pagine aspx denominate OrderList.aspx e l'altra denominata OrderDetails.aspx

OrderList.aspx sfrutta i controlli GridView e EntityDataSource, come in precedenza.

<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 seleziona i record della tabella Orders filtrati in UserName (vedere WhereParameter) che è stato impostato in una variabile di sessione quando si accede al log dell'utente.

Si noti anche questi parametri nel Campo ipertestuale di GridView:

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

Questi specificano il collegamento alla visualizzazione Dettagli ordine per ogni prodotto che specifica il campo OrderID come parametro QueryString nella pagina OrderDetails.aspx.

OrderDetails.aspx

Verrà usato un controllo EntityDataSource per accedere agli Ordini e a FormView per visualizzare i dati order e un'altra EntityDataSource con GridView per visualizzare tutti gli elementi della riga dell'ordine.

<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>

Nel file Code Behind (OrderDetails.aspx.cs) sono disponibili due bit di pulizia.

Prima di tutto è necessario assicurarsi che OrderDetails ottenga sempre un OrderId.

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

È anche necessario calcolare e visualizzare il totale dell'ordine dagli elementi della riga.

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");
   }
}

Home page

Aggiungere un contenuto statico alla pagina Default.aspx.

Prima di tutto creerò una cartella "Contenuto" e all'interno di una cartella Images (e includerò un'immagine da usare nella home page).

Nel segnaposto inferiore della pagina Default.aspx aggiungere il markup seguente.

<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>

Recensioni dei prodotti

Prima di tutto verrà aggiunto un pulsante con un collegamento a un modulo che è possibile usare per immettere una revisione del prodotto.

<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>

Screenshot che mostra la posizione del collegamento.

Si noti che il ProductID viene passato nella stringa di query

Aggiungiamo quindi una pagina denominata ReviewAdd.aspx

Questa pagina userà ASP.NET AJAX Control Toolkit. Se non è già stato fatto, è possibile scaricarlo da DevExpress e sono disponibili indicazioni sulla configurazione del toolkit da usare con Visual Studio qui https://www.asp.net/learn/ajax-videos/video-76.aspx.

In modalità di progettazione trascinare i controlli e i validator dalla casella degli strumenti e compilare un modulo simile a quello seguente.

Screenshot che mostra il modulo.

Il markup avrà un aspetto simile al seguente.

<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>

Ora che è possibile immettere le recensioni, consente di visualizzare le recensioni nella pagina del prodotto.

Aggiungere questo markup alla pagina 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>

Eseguire l'applicazione ora e passare a un prodotto mostra le informazioni sul prodotto, incluse le recensioni dei clienti.

Screenshot che mostra le recensioni dei clienti.

Controllo elementi comuni (creazione di controlli utente)

Per aumentare le vendite sul tuo sito Web, aggiungeremo un paio di funzionalità a "vendita suggestive" prodotti popolari o correlati.

La prima di queste funzionalità sarà un elenco del prodotto più popolare nel nostro catalogo prodotti.

Verrà creato un "Controllo utente" per visualizzare gli elementi più venduti nella home page dell'applicazione. Poiché questo sarà un controllo, è possibile usarlo in qualsiasi pagina semplicemente trascinando e eliminando il controllo nella finestra di progettazione di Visual Studio in qualsiasi pagina che ci piace.

In Esplora soluzioni di Visual Studio fare clic con il pulsante destro del mouse sul nome della soluzione e creare una nuova directory denominata "Controls". Anche se non è necessario farlo, il progetto verrà mantenuto organizzato creando tutti i controlli utente nella directory "Controls".

Fare clic con il pulsante destro del mouse sulla cartella controlli e scegliere "Nuovo elemento" :

Screenshot che mostra dove selezionare Nuovo elemento.

Specificare un nome per il controllo "PopularItems". Si noti che l'estensione del file per i controlli utente è .ascx non .aspx.

Il controllo Utenti elementi comuni verrà definito come indicato di seguito.

<%@ 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>

Di seguito viene usato un metodo che non è ancora stato usato in questa applicazione. Si usa il controllo ripetitore e invece di usare un controllo origine dati che associa il controllo Ripetitore ai risultati di una query di LINQ to Entities.

Nel code behind del controllo è possibile eseguire questa operazione come indicato di seguito.

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);
      }
    }
}

Si noti anche questa riga importante nella parte superiore del markup del controllo.

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

Poiché gli elementi più diffusi non verranno modificati in base a minuti, è possibile aggiungere una direttiva con estensione per migliorare le prestazioni dell'applicazione. Questa direttiva causerà l'esecuzione del codice dei controlli solo quando scade l'output memorizzato nella cache del controllo. In caso contrario, verrà usata la versione memorizzata nella cache dell'output del controllo.

Ora è necessario includere il nuovo controllo nella pagina Default.aspx.

Usare il trascinamento e l'elenco a discesa per inserire un'istanza del controllo nella colonna aperta del modulo predefinito.

Screenshot che mostra dove inserire un'istanza del controllo.

Quando si esegue l'applicazione, la home page visualizza gli elementi più diffusi.

Screenshot che mostra come la home page visualizza gli elementi più popolari.

Controllo "Anche acquistato" (controlli utente con parametri)

Il secondo controllo utente che creeremo porterà a vendere in modo suggestivo al livello successivo aggiungendo la specificità del contesto.

La logica per calcolare gli elementi "Anche acquistati" è non semplice.

Il controllo "Acquistato" selezionerà i record OrderDetails (acquistati in precedenza) per l'ID ProductID attualmente selezionato e afferrare gli OrderID per ogni ordine univoco trovato.

Verranno quindi selezionati i prodotti da tutti gli Ordini e sommare le quantità acquistate. Verranno ordinati i prodotti in base alla somma della quantità e verranno visualizzati i primi cinque elementi.

Dato la complessità di questa logica, verrà implementato questo algoritmo come stored procedure.

Il T-SQL per la stored procedure è il seguente.

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

Si noti che questa stored procedure (SelectPurchasedWithProducts) esiste nel database quando è stata inclusa nell'applicazione e quando è stato generato il modello di dati di entità specificato che, oltre alle tabelle e alle viste necessarie, il modello di dati di entità deve includere questa stored procedure.

Per accedere alla stored procedure dal modello di dati di entità, è necessario importare la funzione.

Fare doppio clic sul modello di dati di entità in Esplora soluzioni per aprirlo nella finestra di progettazione e aprire il Browser modelli, quindi fare clic con il pulsante destro del mouse nella finestra di progettazione e selezionare "Aggiungi importazione funzione".

Screenshot che mostra dove selezionare Aggiungi importazione funzione.

In questo modo verrà aperta questa finestra di dialogo.

Screenshot che mostra la finestra di dialogo aperta.

Compilare i campi indicati in precedenza, selezionando "SelectPurchasedWithProducts" e usare il nome della procedura per il nome della funzione importata.

Fare clic su "Ok".

Dopo aver eseguito questa operazione, è possibile semplicemente programmare la stored procedure in quanto è possibile che qualsiasi altro elemento nel modello.

Quindi, nella cartella "Controls" creare un nuovo controllo utente denominato AlsoPurchased.ascx.

Il markup per questo controllo avrà un aspetto molto familiare al controllo 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>

La differenza notevole è che non memorizza nella cache l'output perché il rendering dell'elemento sarà diverso per prodotto.

ProductId sarà una "proprietà" per il controllo.

private int _ProductId;

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

Nel gestore eventi PreRender del controllo è necessario eseguire tre operazioni.

  1. Assicurarsi che ProductID sia impostato.
  2. Vedere se sono presenti prodotti acquistati con quello corrente.
  3. Restituisce alcuni elementi come determinato in #2.

Si noti come è facile chiamare la stored procedure tramite il modello.

//--------------------------------------------------------------------------------------+
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();
     }
}

Dopo aver determinato che ci SONO anche "acquistati" è possibile associare semplicemente il ripetitore ai risultati restituiti dalla query.

//-------------------------------------------------------------------------------------+
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);
          }
        }
}

Se non ci sono articoli "acquistati anche" verranno semplicemente visualizzati altri articoli popolari dal nostro catalogo.

//--------------------------------------------------------------------------------------+
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);
      }
    }
}

Per visualizzare gli elementi "Acquistati anche", aprire la pagina ProductDetails.aspx e trascinare il controllo AlsoPurchased da Esplora soluzioni in modo che venga visualizzato in questa posizione nel markup.

<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>

In questo modo verrà creato un riferimento al controllo nella parte superiore della pagina ProductDetails.

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

Poiché il controllo utente AlsoPurchased richiede un numero ProductId, verrà impostata la proprietà ProductID del controllo usando un'istruzione Eval sull'elemento del modello di dati corrente della pagina.

Screenshot che evidenzia il prodotto ID.

Quando si compilano ed eseguono ora e si passa a un prodotto, vengono visualizzati gli articoli "Acquistati anche".

Screenshot che mostra anche gli elementi acquistati.