Część 7. Dodawanie funkcji
Autor : Joe Stagner
Tailspin Spyworks pokazuje, jak niezwykle proste jest tworzenie zaawansowanych, skalowalnych aplikacji dla platformy .NET. Pokazuje, jak używać wspaniałych nowych funkcji w ASP.NET 4 do tworzenia sklepu internetowego, w tym zakupów, wyewidencjonowania i administracji.
W tej serii samouczków szczegółowo opisano wszystkie kroki wykonywane w celu utworzenia przykładowej aplikacji Tailspin Spyworks. Część 7 dodaje dodatkowe funkcje, takie jak przegląd konta, przeglądy produktów i "popularne elementy", a także kontrolki użytkownika "zakupione".
Dodawanie funkcji
Chociaż użytkownicy mogą przeglądać nasz katalog, umieszczać elementy w koszyku i ukończyć proces finalizacji zakupu, dostępnych jest wiele funkcji pomocniczych, które będziemy uwzględniać w celu ulepszenia naszej witryny.
- Przegląd konta (zamówienie listy złożone i wyświetlanie szczegółów).
- Dodaj pewną zawartość specyficzną dla kontekstu do strony głównej.
- Dodaj funkcję, aby umożliwić użytkownikom przeglądanie produktów w katalogu.
- Utwórz kontrolkę użytkownika, aby wyświetlić popularne elementy i umieść kontrolkę na stronie głównej.
- Utwórz kontrolkę użytkownika "Również zakupione" i dodaj ją do strony szczegółów produktu.
- Dodaj stronę kontaktu.
- Dodaj stronę Informacje.
- Błąd globalny
Przegląd konta
W folderze "Account" utwórz dwie strony aspx o nazwie OrderList.aspx i inne o nazwie OrderDetails.aspx
Funkcja OrderList.aspx będzie korzystać z kontrolek GridView i EntityDataSource tak samo jak poprzednio.
<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>
Element EntityDataSource wybiera rekordy z tabeli Orders odfiltrowane według parametru UserName (zobacz WhereParameter), które ustawiamy w zmiennej sesji, gdy loguje się użytkownik.
Zwróć również uwagę na następujące parametry w obszarze HyperlinkField kontrolki GridView:
DataNavigateUrlFields="OrderID" DataNavigateUrlFormatString="~/Account/OrderDetails.aspx?OrderID={0}"
Określają one link do widoku Szczegóły zamówienia dla każdego produktu, określając pole OrderID jako parametr QueryString na stronie OrderDetails.aspx.
OrderDetails.aspx
Użyjemy kontrolki EntityDataSource, aby uzyskać dostęp do elementów Orders i FormView w celu wyświetlenia danych zamówienia i innej jednostki EntityDataSource z kontrolką GridView, aby wyświetlić wszystkie elementy wiersza zamówienia.
<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>
W pliku Code Behind (OrderDetails.aspx.cs) mamy dwa małe fragmenty sprzątania.
Najpierw musimy upewnić się, że element OrderDetails zawsze otrzymuje identyfikator OrderId.
protected void Page_Load(object sender, EventArgs e)
{
if (String.IsNullOrEmpty(Request.QueryString["OrderId"]))
{
Response.Redirect("~/Account/OrderList.aspx");
}
}
Musimy również obliczyć i wyświetlić sumę zamówienia z elementów wiersza.
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");
}
}
Strona główna
Dodajmy zawartość statyczną do strony Default.aspx.
Najpierw utworzym folder "Zawartość" i w nim folder Images (i dołączę obraz do użycia na stronie głównej).
W dolnym symbolu zastępczym strony Default.aspx dodaj następujący znacznik.
<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>
Recenzje produktów
Najpierw dodamy przycisk z linkiem do formularza, którego możemy użyć do wprowadzenia przeglądu produktu.
<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>
Zwróć uwagę, że przekazujemy identyfikator ProductID w ciągu zapytania
Następnie dodajmy stronę o nazwie ReviewAdd.aspx
Ta strona będzie używać zestawu narzędzi ASP.NET AJAX Control Toolkit. Jeśli jeszcze tego nie zrobiono, możesz pobrać go z witryny DevExpress i znajdziesz wskazówki dotyczące konfigurowania zestawu narzędzi do użycia z programem Visual Studio tutaj https://www.asp.net/learn/ajax-videos/video-76.aspx.
W trybie projektowania przeciągnij kontrolki i moduły sprawdzania poprawności z przybornika i skompiluj formularz podobny do poniższego.
Znaczniki będą wyglądać mniej więcej tak.
<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>
Teraz, po wprowadzeniu recenzji, możemy wyświetlić te recenzje na stronie produktu.
Dodaj ten znacznik do strony 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>
Uruchomienie naszej aplikacji teraz i przejście do produktu pokazuje informacje o produkcie, w tym recenzje klientów.
Kontrolka Popularne elementy (tworzenie kontrolek użytkownika)
Aby zwiększyć sprzedaż w witrynie internetowej, dodamy kilka funkcji do "sugestywnej sprzedaży" popularnych lub powiązanych produktów.
Pierwsza z tych funkcji będzie listą bardziej popularnych produktów w naszym katalogu produktów.
Utworzymy "Kontrolkę użytkownika", aby wyświetlić elementy najlepiej sprzedających się na stronie głównej naszej aplikacji. Ponieważ będzie to kontrolka, możemy jej użyć na dowolnej stronie, przeciągając i upuszczając kontrolkę w projektancie programu Visual Studio na dowolnej stronie, którą lubimy.
W Eksploratorze rozwiązań programu Visual Studio kliknij prawym przyciskiem myszy nazwę rozwiązania i utwórz nowy katalog o nazwie "Controls". Chociaż nie jest to konieczne, pomożemy utrzymać organizację naszego projektu przez utworzenie wszystkich kontrolek użytkowników w katalogu "Kontrolki".
Kliknij prawym przyciskiem myszy folder controls i wybierz polecenie "Nowy element":
Określ nazwę kontrolki "PopularItems". Należy pamiętać, że rozszerzenie pliku dla kontrolek użytkownika to .ascx, a nie .aspx.
Nasza kontrolka Popularne elementy Kontrolka użytkownika zostanie zdefiniowana w następujący sposób.
<%@ 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>
W tym miejscu używamy metody, która nie była jeszcze używana w tej aplikacji. Używamy kontrolki powtarzania i zamiast używać kontrolki źródła danych, wiążemy kontrolkę Repeater z wynikami zapytania LINQ to Entities.
W kodzie naszej kontroli robimy to w następujący sposób.
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);
}
}
}
Zwróć również uwagę na ten ważny wiersz u góry znaczników kontrolki.
<%@ OutputCache Duration="3600" VaryByParam="None" %>
Ponieważ najpopularniejsze elementy nie będą zmieniane od minuty do minuty, możemy dodać dyrektywę o buforowaniu, aby poprawić wydajność naszej aplikacji. Ta dyrektywa spowoduje wykonanie kodu kontrolek tylko po wygaśnięciu buforowanych danych wyjściowych kontrolki. W przeciwnym razie zostanie użyta buforowana wersja danych wyjściowych kontrolki.
Teraz wystarczy dołączyć nową kontrolkę na stronie Default.aspx.
Użyj przeciągania i upuszczania, aby umieścić wystąpienie kontrolki w otwartej kolumnie formularza domyślnego.
Teraz, gdy uruchomimy aplikację, strona główna wyświetla najpopularniejsze elementy.
Kontrolka "Również zakupione" (kontrolki użytkownika z parametrami)
Druga kontrolka użytkownika, którą utworzymy, podejmie sugerowaną sprzedaż do następnego poziomu, dodając specyfikę kontekstu.
Logika obliczania pierwszych elementów "Również zakupione" nie jest trywialna.
Nasza kontrolka "Również zakupione" wybierze rekordy OrderDetails (wcześniej zakupione) dla aktualnie wybranego identyfikatora ProductID i pobierze identyfikatory OrderID dla każdego znalezionego unikatowego zamówienia.
Następnie wybierzemy wszystkie produkty ze wszystkich tych zamówień i zsumujemy zakupione ilości. Posortujemy produkty według tej sumy ilości i wyświetlimy pięć pierwszych elementów.
Ze względu na złożoność tej logiki zaimplementujemy ten algorytm jako procedurę składowaną.
Język T-SQL dla procedury składowanej jest następujący.
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
Należy pamiętać, że ta procedura składowana (SelectPurchasedWithProducts) istniała w bazie danych, gdy uwzględniliśmy ją w naszej aplikacji, a kiedy wygenerowaliśmy model danych jednostki, określono, że oprócz potrzebnych tabel i widoków model danych jednostki powinien zawierać tę procedurę składowaną.
Aby uzyskać dostęp do procedury składowanej z modelu danych jednostki, musimy zaimportować funkcję.
Kliknij dwukrotnie model danych jednostki w Eksploratorze rozwiązań, aby otworzyć go w projektancie i otworzyć przeglądarkę modelu, a następnie kliknij prawym przyciskiem myszy w projektancie i wybierz pozycję "Dodaj import funkcji".
Spowoduje to otwarcie tego okna dialogowego.
Wypełnij pola, jak pokazano powyżej, wybierając pozycję "SelectPurchasedWithProducts" i użyj nazwy procedury dla nazwy zaimportowanej funkcji.
Kliknij przycisk "OK".
Po wykonaniu tej czynności możemy po prostu zaprogramować względem procedury składowanej, ponieważ możemy użyć dowolnego innego elementu w modelu.
Dlatego w folderze "Controls" utwórz nową kontrolkę użytkownika o nazwie AlsoPurchased.ascx.
Znaczniki dla tej kontrolki będą wyglądać bardzo znajomo dla kontrolki 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>
Godna uwagi różnica polega na tym, że nie buforuje danych wyjściowych, ponieważ elementy do renderowania będą się różnić w zależności od produktu.
Identyfikator produktu będzie "właściwością" kontrolki.
private int _ProductId;
public int ProductId
{
get { return _ProductId ; }
set { _ProductId = Convert.ToInt32(value); }
}
W procedurze obsługi zdarzeń preRender kontrolki wykonaliśmy trzy czynności.
- Upewnij się, że ustawiono wartość ProductID.
- Sprawdź, czy istnieją jakieś produkty zakupione w ramach bieżącej wersji.
- Wyprowadź dane wyjściowe niektórych elementów określonych w #2.
Zwróć uwagę, jak łatwo jest wywołać procedurę składowaną za pomocą modelu.
//--------------------------------------------------------------------------------------+
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();
}
}
Po określeniu, że istnieje "również zakupione" możemy po prostu powiązać repeater z wynikami zwróconych przez zapytanie.
//-------------------------------------------------------------------------------------+
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);
}
}
}
Jeśli nie było żadnych elementów "również zakupionych", po prostu wyświetlimy inne popularne elementy z naszego katalogu.
//--------------------------------------------------------------------------------------+
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);
}
}
}
Aby wyświetlić elementy "Również zakupione", otwórz stronę ProductDetails.aspx i przeciągnij kontrolkę AlsoPurchased z Eksploratora rozwiązań, aby była wyświetlana w tej pozycji na znacznikach.
<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>
Spowoduje to utworzenie odwołania do kontrolki w górnej części strony ProductDetails.
<%@ Register src="Controls/AlsoPurchased.ascx" tagname="AlsoPurchased" tagprefix="uc1" %>
Ponieważ kontrolka użytkownika AlsoPurchased wymaga numeru ProductId, ustawimy właściwość ProductID kontrolki przy użyciu instrukcji Eval względem bieżącego elementu modelu danych strony.
Gdy skompilujemy i uruchomimy teraz i przejdźmy do produktu, zobaczymy elementy "Również zakupione".