第 5 部分:商務邏輯
Tailspin Spyworks 示範如何為 .NET 平臺建立強大且可調整的應用程式,非常簡單。 其中顯示如何使用 ASP.NET 4 中的絕佳新功能來建置線上商店,包括購物、結帳和管理。
本教學課程系列詳細說明建置 Tailspin Spyworks 範例應用程式所採取的所有步驟。 第 5 部分會新增一些商務邏輯。
新增一些商務邏輯
我們希望每當有人造訪我們的網站時,都可以使用我們的購物體驗。 即使訪客未註冊或登入,訪客還是能夠流覽並新增專案至購物車。 當他們準備好簽出時,系統會提供驗證的選項,如果它們還不是成員,他們就能夠建立帳戶。
這表示我們必須實作邏輯,將購物車從匿名狀態轉換成「已註冊的使用者」狀態。
讓我們建立名為 「Classes」 的目錄,然後在資料夾上Right-Click,然後建立名為 MyShoppingCart.cs 的新「類別」檔案
如先前所述,我們將擴充實作 MyShoppingCart.aspx 頁面的 類別,我們會使用 來執行此動作。NET 的強大「部分類別」建構。
為 MyShoppingCart.aspx.cf 檔案產生的呼叫看起來像這樣。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace TailspinSpyworks
{
public partial class MyShoppingCart : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
}
}
請注意使用 「partial」 關鍵字。
我們剛才產生的類別檔案看起來像這樣。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace TailspinSpyworks.Classes
{
public class MyShoppingCart
{
}
}
我們也會將 partial 關鍵字新增至此檔案,以合併我們的實作。
我們的新類別檔案現在看起來像這樣。
namespace TailspinSpyworks.Classes
{
public partial class MyShoppingCart
{
}
}
我們將新增至類別的第一個方法是 「AddItem」 方法。 這是當使用者按一下 [產品清單] 和 [產品詳細資料] 頁面上的 [新增至藝術] 連結時,最終會呼叫的方法。
將下列內容附加至頁面頂端的 using 語句。
using TailspinSpyworks.Data_Access;
並將這個方法新增至 MyShoppingCart 類別。
//------------------------------------------------------------------------------------+
public void AddItem(string cartID, int productID, int quantity)
{
using (CommerceEntities db = new CommerceEntities())
{
try
{
var myItem = (from c in db.ShoppingCarts where c.CartID == cartID &&
c.ProductID == productID select c).FirstOrDefault();
if(myItem == null)
{
ShoppingCart cartadd = new ShoppingCart();
cartadd.CartID = cartID;
cartadd.Quantity = quantity;
cartadd.ProductID = productID;
cartadd.DateCreated = DateTime.Now;
db.ShoppingCarts.AddObject(cartadd);
}
else
{
myItem.Quantity += quantity;
}
db.SaveChanges();
}
catch (Exception exp)
{
throw new Exception("ERROR: Unable to Add Item to Cart - " +
exp.Message.ToString(), exp);
}
}
}
我們使用LINQ to Entities來查看專案是否已在購物車中。 如果是,我們會更新專案的訂單數量,否則我們會為選取的專案建立新專案
為了呼叫這個方法,我們將實作 AddToCart.aspx 頁面,該頁面不僅將此方法分類,還會在新增專案之後顯示目前的購物 a=cart。
Right-Click方案總管中的方案名稱,並新增名為 AddToCart.aspx 的新頁面,如同我們先前所做的一樣。
雖然我們可以使用此頁面在實作中顯示短期結果,例如低股票問題等等,但頁面不會實際轉譯,而是呼叫「新增」邏輯和重新導向。
為了達成此目的,我們會將下列程式碼新增至 Page_Load 事件。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Diagnostics;
namespace TailspinSpyworks
{
public partial class AddToCart : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string rawId = Request.QueryString["ProductID"];
int productId;
if (!String.IsNullOrEmpty(rawId) && Int32.TryParse(rawId, out productId))
{
MyShoppingCart usersShoppingCart = new MyShoppingCart();
String cartId = usersShoppingCart.GetShoppingCartId();
usersShoppingCart.AddItem(cartId, productId, 1);
}
else
{
Debug.Fail("ERROR : We should never get to AddToCart.aspx
without a ProductId.");
throw new Exception("ERROR : It is illegal to load AddToCart.aspx
without setting a ProductId.");
}
Response.Redirect("MyShoppingCart.aspx");
}
}
}
請注意,我們會從 QueryString 參數擷取要新增至購物車的產品,並呼叫 類別的 AddItem 方法。
假設沒有發生任何錯誤的控制項會傳遞至 SHoppingCart.aspx 頁面,我們將在下一步完整實作。 如果應該發生錯誤,我們會擲回例外狀況。
我們目前尚未實作全域錯誤處理常式,因此我們的應用程式不會處理此例外狀況,但我們很快就會解決這個問題。
另請注意,使用可透過 提供的語句 Debug.Fail () (using System.Diagnostics;)
應用程式是在偵錯工具內執行,這個方法會顯示詳細的對話方塊,其中包含應用程式狀態的相關資訊,以及我們指定的錯誤訊息。
在生產環境中執行時,會忽略 Debug.Fail () 語句。
您會在上述程式碼中記下我們購物車類別名稱 「GetShoppingCartId」 中方法的呼叫。
新增程式碼以實作 方法,如下所示。
請注意,我們也新增了更新和結帳按鈕,以及可顯示購物車「總計」的標籤。
public const string CartId = "TailSpinSpyWorks_CartID";
//--------------------------------------------------------------------------------------+
public String GetShoppingCartId()
{
if (Session[CartId] == null)
{
Session[CartId] = System.Web.HttpContext.Current.Request.IsAuthenticated ?
User.Identity.Name : Guid.NewGuid().ToString();
}
return Session[CartId].ToString();
}
我們現在可以將專案新增至購物車,但尚未實作在新增產品之後顯示購物車的邏輯。
因此,在 MyShoppingCart.aspx 頁面中,我們將新增 EntityDataSource 控制項和 GridVire 控制項,如下所示。
<div id="ShoppingCartTitle" runat="server" class="ContentHead">Shopping Cart</div>
<asp:GridView ID="MyList" runat="server" AutoGenerateColumns="False" ShowFooter="True"
GridLines="Vertical" CellPadding="4"
DataSourceID="EDS_Cart"
DataKeyNames="ProductID,UnitCost,Quantity"
CssClass="CartListItem">
<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:TemplateField>
<HeaderTemplate>Quantity</HeaderTemplate>
<ItemTemplate>
<asp:TextBox ID="PurchaseQuantity" Width="40" runat="server"
Text='<%# Bind("Quantity") %>'></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
<HeaderTemplate>Item Total</HeaderTemplate>
<ItemTemplate>
<%# (Convert.ToDouble(Eval("Quantity")) *
Convert.ToDouble(Eval("UnitCost")))%>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
<HeaderTemplate>Remove Item</HeaderTemplate>
<ItemTemplate>
<center>
<asp:CheckBox id="Remove" runat="server" />
</center>
</ItemTemplate>
</asp:TemplateField>
</Columns>
<FooterStyle CssClass="CartListFooter"/>
<HeaderStyle CssClass="CartListHead" />
</asp:GridView>
<div>
<strong>
<asp:Label ID="LabelTotalText" runat="server" Text="Order Total : ">
</asp:Label>
<asp:Label CssClass="NormalBold" id="lblTotal" runat="server"
EnableViewState="false">
</asp:Label>
</strong>
</div>
<br />
<asp:imagebutton id="UpdateBtn" runat="server" ImageURL="Styles/Images/update_cart.gif"
onclick="UpdateBtn_Click"></asp:imagebutton>
<asp:imagebutton id="CheckoutBtn" runat="server"
ImageURL="Styles/Images/final_checkout.gif"
PostBackUrl="~/CheckOut.aspx">
</asp:imagebutton>
<asp:EntityDataSource ID="EDS_Cart" runat="server"
ConnectionString="name=CommerceEntities"
DefaultContainerName="CommerceEntities" EnableFlattening="False"
EnableUpdate="True" EntitySetName="ViewCarts"
AutoGenerateWhereClause="True" EntityTypeFilter="" Select=""
Where="">
<WhereParameters>
<asp:SessionParameter Name="CartID" DefaultValue="0"
SessionField="TailSpinSpyWorks_CartID" />
</WhereParameters>
</asp:EntityDataSource>
在設計工具中呼叫表單,以便按兩下 [更新購物車] 按鈕,並產生標記中宣告中指定的 Click 事件處理常式。
我們稍後會實作詳細資料,但這麼做可讓我們建置並執行應用程式,而不會發生錯誤。
當您執行應用程式並將專案新增至購物車時,您會看到此專案。
請注意,我們已藉由實作三個自訂資料行,從「預設」方格顯示中脫離。
第一個是 Quantity 的 [可編輯]、[系結] 欄位:
<asp:TemplateField>
<HeaderTemplate>Quantity</HeaderTemplate>
<ItemTemplate>
<asp:TextBox ID="PurchaseQuantity" Width="40" runat="server"
Text='<%# Bind("Quantity") %>'></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
下一個是「計算」資料行,顯示明細專案總計, (專案成本時間要排序的數量) :
<asp:TemplateField>
<HeaderTemplate>Item Total</HeaderTemplate>
<ItemTemplate>
<%# (Convert.ToDouble(Eval("Quantity")) *
Convert.ToDouble(Eval("UnitCost")))%>
</ItemTemplate>
</asp:TemplateField>
最後,我們有一個自訂資料行,其中包含一個 CheckBox 控制項,使用者將會用來指出專案應該從購物圖表中移除。
<asp:TemplateField>
<HeaderTemplate>Remove Item</HeaderTemplate>
<ItemTemplate>
<center>
<asp:CheckBox id="Remove" runat="server" />
</center>
</ItemTemplate>
</asp:TemplateField>
如您所見,Order Total 行是空的,因此讓我們新增一些邏輯來計算 Order Total。
我們會先對 MyShoppingCart 類別實作 「GetTotal」 方法。
在 MyShoppingCart.cs 檔案中,新增下列程式碼。
//--------------------------------------------------------------------------------------+
public decimal GetTotal(string cartID)
{
using (CommerceEntities db = new CommerceEntities())
{
decimal cartTotal = 0;
try
{
var myCart = (from c in db.ViewCarts where c.CartID == cartID select c);
if (myCart.Count() > 0)
{
cartTotal = myCart.Sum(od => (decimal)od.Quantity * (decimal)od.UnitCost);
}
}
catch (Exception exp)
{
throw new Exception("ERROR: Unable to Calculate Order Total - " +
exp.Message.ToString(), exp);
}
return (cartTotal);
}
}
然後在 Page_Load 事件處理常式中,我們可以呼叫 GetTotal 方法。 同時,我們將新增測試,以查看購物車是否空白,並據以調整顯示。
現在,如果購物車是空的,我們就會得到:
如果沒有,我們就會看到我們的總計。
不過,此頁面尚未完成。
我們需要額外的邏輯來重新計算購物車,方法是移除標示要移除的專案,以及判斷新的數量詞,因為使用者可能已在方格中變更某些值。
讓我們將 「RemoveItem」 方法新增至 MyShoppingCart.cs 中的購物車類別,以處理使用者標示要移除的專案時的情況。
//------------------------------------------------------------------------------------+
public void RemoveItem(string cartID, int productID)
{
using (CommerceEntities db = new CommerceEntities())
{
try
{
var myItem = (from c in db.ShoppingCarts where c.CartID == cartID &&
c.ProductID == productID select c).FirstOrDefault();
if (myItem != null)
{
db.DeleteObject(myItem);
db.SaveChanges();
}
}
catch (Exception exp)
{
throw new Exception("ERROR: Unable to Remove Cart Item - " +
exp.Message.ToString(), exp);
}
}
}
現在讓我們廣告一種方法來處理使用者只是變更在 GridView 中排序品質的情況。
//--------------------------------------------------------------------------------------+
public void UpdateItem(string cartID, int productID, int quantity)
{
using (CommerceEntities db = new CommerceEntities())
{
try
{
var myItem = (from c in db.ShoppingCarts where c.CartID == cartID &&
c.ProductID == productID select c).FirstOrDefault();
if (myItem != null)
{
myItem.Quantity = quantity;
db.SaveChanges();
}
}
catch (Exception exp)
{
throw new Exception("ERROR: Unable to Update Cart Item - " +
exp.Message.ToString(), exp);
}
}
}
有了基本 Remove 和 Update 功能,我們可以實作實際更新資料庫中購物車的邏輯。 (MyShoppingCart.cs)
//-------------------------------------------------------------------------------------+
public void UpdateShoppingCartDatabase(String cartId,
ShoppingCartUpdates[] CartItemUpdates)
{
using (CommerceEntities db = new CommerceEntities())
{
try
{
int CartItemCOunt = CartItemUpdates.Count();
var myCart = (from c in db.ViewCarts where c.CartID == cartId select c);
foreach (var cartItem in myCart)
{
// Iterate through all rows within shopping cart list
for (int i = 0; i < CartItemCOunt; i++)
{
if (cartItem.ProductID == CartItemUpdates[i].ProductId)
{
if (CartItemUpdates[i].PurchaseQantity < 1 ||
CartItemUpdates[i].RemoveItem == true)
{
RemoveItem(cartId, cartItem.ProductID);
}
else
{
UpdateItem(cartId, cartItem.ProductID,
CartItemUpdates[i].PurchaseQantity);
}
}
}
}
}
catch (Exception exp)
{
throw new Exception("ERROR: Unable to Update Cart Database - " +
exp.Message.ToString(), exp);
}
}
}
您會注意到此方法需要兩個參數。 其中一個是購物車識別碼,另一個是使用者定義類型的物件陣列。
為了將邏輯在使用者介面細節上的相依性降到最低,我們已定義資料結構,我們可用來將購物車專案傳遞至程式碼,而不需要直接存取 GridView 控制項的方法。
public struct ShoppingCartUpdates
{
public int ProductId;
public int PurchaseQantity;
public bool RemoveItem;
}
在我們的 MyShoppingCart.aspx.cs 檔案中,我們可以在 [更新按鈕] 按一下事件處理常式中使用這個結構,如下所示。 請注意,除了更新購物車之外,我們也會更新購物車總計。
//--------------------------------------------------------------------------------------+
protected void UpdateBtn_Click(object sender, ImageClickEventArgs e)
{
MyShoppingCart usersShoppingCart = new MyShoppingCart();
String cartId = usersShoppingCart.GetShoppingCartId();
ShoppingCartUpdates[] cartUpdates = new ShoppingCartUpdates[MyList.Rows.Count];
for (int i = 0; i < MyList.Rows.Count; i++)
{
IOrderedDictionary rowValues = new OrderedDictionary();
rowValues = GetValues(MyList.Rows[i]);
cartUpdates[i].ProductId = Convert.ToInt32(rowValues["ProductID"]);
cartUpdates[i].PurchaseQantity = Convert.ToInt32(rowValues["Quantity"]);
CheckBox cbRemove = new CheckBox();
cbRemove = (CheckBox)MyList.Rows[i].FindControl("Remove");
cartUpdates[i].RemoveItem = cbRemove.Checked;
}
usersShoppingCart.UpdateShoppingCartDatabase(cartId, cartUpdates);
MyList.DataBind();
lblTotal.Text = String.Format("{0:c}", usersShoppingCart.GetTotal(cartId));
}
請注意,這一行程式碼特別感興趣:
rowValues = GetValues(MyList.Rows[i]);
GetValues () 是特殊的協助程式函式,我們將在 MyShoppingCart.aspx.cs 中實作,如下所示。
//--------------------------------------------------------------------------------------+
public static IOrderedDictionary GetValues(GridViewRow row)
{
IOrderedDictionary values = new OrderedDictionary();
foreach (DataControlFieldCell cell in row.Cells)
{
if (cell.Visible)
{
// Extract values from the cell
cell.ContainingField.ExtractValuesFromCell(values, cell, row.RowState, true);
}
}
return values;
}
這可讓您清楚存取 GridView 控制項中系結元素的值。 由於我們的 「移除專案」CheckBox 控制項未系結,因此我們會透過 FindControl () 方法來存取它。
在專案的開發階段中,我們已準備好實作結帳程式。
這麼做之前,讓我們使用 Visual Studio 來產生成員資格資料庫,並將使用者新增至成員資格存放庫。