開發BtoC電子商務系統(ASP.NET)

來源:互聯網
上載者:User
原文還有兩個圖
在對ASP.NET Web表單的編程模型有了基本的認識後,通過應用於現實的開發案例來提高對ASP.NET Web表單內在運作機制的瞭解,以及由此帶來的對系統架構的掌控是很有必要的。我們沒有為編程而編程的高貴姿態,我們深深懂得能夠開發出高效,健壯,強大的應用程式始終是編程的終極。我們下面通過一個完整的BToC電子商務系統的開發流程來展示ASP.NET Web表單是怎樣具體搭建面向下一代網路平台的。
這是一個典型的基於B/S(瀏覽器/伺服器) 三層架構的食品,飲料電子商務零售系統——“玉米地零食店”。前端為產品瀏覽器,為消費者提供瀏覽/選購商品,下訂單購物等各個環節的功能;中介層為銷售商的稅率,優惠等商務邏輯;後端為與整個零售系統相關的產品,顧客,訂單等資料庫。我們採用ASP.NET+IIS 5來構建前端和中介層,SQL Server 2000來管理後端資料庫,整個系統運行於Windows XP。相關硬體設定只要滿足上述軟體的基本配置,系統效能便可保證。下面為該網上零售系統的前端介面圖示:

在編製Web 表單商業前端和中介層之前,我們有必要對後端資料庫做一個簡單的介紹。後端資料庫 CornfieldGrocer 由4個表組成:產品類別表Categories ,產品細節表 Details ,產品表 Products ,客戶資訊表Customers。考慮到示範系統的的簡潔性,我們沒有添加相關的預存程序,視圖,規則等,這些在實際的系統的開發中對提高系統的效能是很有必要,尤其是在大資料量的情況下。下面為4個表的欄位的圖示介紹:

各個表的欄位的表義已經相當清楚,我們不在這裡贅述。我們下面向大家展示一下整個電子商務零售系統——“玉米地零食店”的物理檔案組成及其結構,下圖為示意圖:

所有的檔案位於ASP.NET網站目錄CornfieldGrocer下,其中還有Web表單頁面用到的圖片子目錄Images下的檔案就不再在這裡列出了。
下面我們開始編寫前端和中介層代碼,為了更清楚地展示Web Form ASP.NET的底層代碼構造,我們採用記事本來完成整個代碼的編寫過程。需要說明的是在真正的工程項目開發實踐中,如能藉助Visual Studio.Net等可視整合開發工具,開發效率會大大提高。但在ASP.NET代碼的底層機制沒有諳熟的情況下,筆者強烈建議初期的開發學習不妨放在Windows系統內建的“記事本”這一簡單卻能夠把代碼暴露得相當清晰的工具裡。
由於篇幅有限,我們不可能將所有的代碼都在這裡展示給大家。如前所述,web.config為每個網站級的基於XML的設定檔,負責一些ASP.NET的安全認證,編碼選擇,診斷測試等ASP.NET的配置工作,為瀏覽器請求ASP.NET Web表單時通過 IIS處理後的第一站。下面為其內容:

<configuration>
<system.web>
<globalization requestEncoding="UTF-8" responseEncoding="UTF-8" />
</system.web>
</configuration>


容易看到這裡的配置內容相當簡單,僅指定請求/發送的編碼為“UTF-8”。我們對此不再贅述。
global.asax檔案及其由後端代碼檔案global.asax.cs編譯成的Bin\CornfieldGrocer.dll共同組成該網上零售系統的ASP.NET應用程式定義。我們先來看檔案global.asax:

<%@ Application Inherits="CornfieldGrocer.Global" %>

該檔案只有一行指示符,它表示ASP.NET應用程式的定義繼承自Global類,而Global類正是在global.asax.cs檔案中定義:

using System;
using System.Collections;
using System.ComponentModel;
using System.Web;
using System.Web.SessionState;

namespace CornfieldGrocer
{
public class Global : System.Web.HttpApplication
{
protected void Session_Start(Object sender, EventArgs e)
{
if (Session["ShoppingCart"] == null)
{
Session["ShoppingCart"] = new CornfieldGrocer.OrderList();
}
}
}
}

在Global類裡,我們定義了區段(Session)意義下的購物卡(ShoppingCart)——這裡採用了C#中的索引器。購物卡的類型為CornfieldGrocer命名空間中的OrderList類,在CornfieldGrocer.cs檔案中有定義。我們當然也可以在global.asax檔案中用指令碼語言的形式將上面兩個檔案的內容合并起來,但那不是ASP.NET推薦的做法,因為指令碼語言的第一次執行還要進行動態編譯,這回損失一部分效能,而將CS檔案提前編譯成dll檔案則會降低這種代價——當然這裡的編譯的意思還是指將CS的原始碼檔案編譯成微軟中繼語言的過程。其次,頁面與後端代碼分離的原則易於專案管理,是Visual Studio.NET推薦的工程性的做法。
檔案Default.aspx為整個網上零售系統的前端頁面HTML代碼,Default.aspx.cs為其後端控制Web表單行為的CS代碼。由於篇幅關係我們這裡不再贅述其HTML代碼,實際上從前面給出的前端介面圖示,我們可以基本瞭解Default.aspx的HTML代碼結構。Style.css檔案為Default.aspx檔案的頁面樣式定義檔案,定義一些頁面元素的顏色,格式,間距等修飾性的東西,我們也不再多言。下面只向大家展示Default.aspx的頁面指示符:

<%@ AutoEventWireup="false" Inherits="CornfieldGrocer.MainForm" %>

我們用“Inherits="CornfieldGrocer.MainForm"”來表示我們的頁面繼承自MainForm類,這樣我們就實現了對ASP.NET Web 表單行為的控制碼與頁面顯示的HTML的分離。其中“AutoEventWireup="false"”表示頁面事件非自動使能——頁面事件非自動使能的意思是所有頁面事件必須經過使用者明確的操作才能觸發,由於該屬性預設為“true”表示自動使能,但我們的商業邏輯要求非自動使能,故這裡的語句很有必要,否則會引起系統處理的混亂。下面我們來看MainForm類:

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

namespace CornfieldGrocer
{
public class MainForm: System.Web.UI.Page
{
protected System.Web.UI.WebControls.Label CurrentCategory;
protected System.Web.UI.WebControls.Label Name;
protected System.Web.UI.WebControls.Label SubTotal;
protected System.Web.UI.WebControls.ImageButton Imagebutton1;
protected System.Web.UI.WebControls.Label Description;
protected System.Web.UI.WebControls.Label Company;
protected System.Web.UI.WebControls.Repeater DetailsListing;
protected System.Web.UI.WebControls.DataList ProductListing;
protected System.Web.UI.WebControls.DataList ShoppingCartList;
protected System.Web.UI.HtmlControls.HtmlSelect CategoryList;
protected System.Web.UI.WebControls.Button btnSelect;
protected System.Web.UI.WebControls.Label Tax;
protected System.Web.UI.WebControls.Label Total;
protected System.Web.UI.WebControls.ImageButton Imagebutton4;
protected System.Web.UI.WebControls.ImageButton Imagebutton5;
protected System.Web.UI.WebControls.ImageButton Imagebutton6;
protected System.Web.UI.HtmlControls.HtmlInputText Qty;
protected System.Web.UI.HtmlControls.HtmlGenericControl CheckoutPanel;
protected System.Web.UI.HtmlControls.HtmlImage SelectedProdPicture;

public MainForm()
{
Page.Init += new System.EventHandler(Page_Init);
}
private void Page_Load(object sender, System.EventArgs e)
{
if (!IsPostBack)
{
ProductListing.SelectedIndex = 0;

UpdateProducts();
UpdateShoppingCart();
}
}
private void Page_Init(object sender, EventArgs e)
{
InitializeComponent();
}
private void InitializeComponent()
{
this.btnSelect.Click +=
new System.EventHandler(this.CategoryList_Select);
this.ProductListing.SelectedIndexChanged+=
new System.EventHandler(this.ProductListing_Select);
this.Imagebutton1.Click+=
new ImageClickEventHandler(this.AddBtn_Click);
this.Imagebutton4.Click+=
new ImageClickEventHandler(this.Recalculate_Click);
this.Imagebutton6.Click+=
new ImageClickEventHandler(this.ClearCart_Click);
this.Load +=
new System.EventHandler(this.Page_Load);
}
private void CategoryList_Select(Object sender, EventArgs e)
{
CurrentCategory.Text =
CategoryList.Items[CategoryList.SelectedIndex].Text;
UpdateProducts();
}
private void ProductListing_Select(Object sender, EventArgs e)
{
UpdateProducts();
}
private void AddBtn_Click(Object sender, ImageClickEventArgs e)
{
int productID = Int32.Parse
(ProductListing.DataKeys[ProductListing.SelectedIndex].ToString());

InventoryDB market = new InventoryDB();
DataRow product = market.GetProduct(productID);

CornfieldGrocer.OrderList shoppingCart =
((CornfieldGrocer.OrderList) Session["ShoppingCart"]);

shoppingCart.Add(new CornfieldGrocer.OrderItem(productID,
(String) product["ProductName"],
Double.Parse(product["UnitPrice"].ToString()), 1));

UpdateShoppingCart();
}
private void Recalculate_Click(Object sender, ImageClickEventArgs e)
{
CornfieldGrocer.OrderList shoppingCart =
((CornfieldGrocer.OrderList) Session["ShoppingCart"]);

for (int i=0; i<ShoppingCartList.Items.Count; i++) >
{
HtmlInputText qty =
(HtmlInputText) ShoppingCartList.Items[i].FindControl("Qty");
try
{
shoppingCart[(String) ShoppingCartList.DataKeys][i]].Quantity
= Int32.Parse(qty.Value);
}
catch (Exception)
{
}
}
UpdateShoppingCart();
}
private void ClearCart_Click(Object sender, ImageClickEventArgs e)
{
CornfieldGrocer.OrderList shoppingCart =
((CornfieldGrocer.OrderList) Session["ShoppingCart"]);

shoppingCart.ClearCart();
UpdateShoppingCart();
}
void UpdateProducts()
{
InventoryDB market = new InventoryDB();

int categoryID = Int32.Parse
(CategoryList.Items[CategoryList.SelectedIndex].Value);
ProductListing.DataSource =
market.GetProducts(categoryID).DefaultView;
ProductListing.DataBind();

int productID = Int32.Parse
(ProductListing.DataKeys[ProductListing.SelectedIndex].ToString());
DataRow product = market.GetProduct(productID);

Name.Text = product["ProductName"].ToString();
SelectedProdPicture.Src = product["ImagePath"].ToString();
Description.Text = product["ProductDescription"].ToString();
Company.Text = product["Manufacturer"].ToString();

DetailsListing.DataSource =
market.GetProductCalories(productID).DefaultView;
DetailsListing.DataBind();
}
void UpdateShoppingCart()
{
CornfieldGrocer.OrderList shoppingCart =
((CornfieldGrocer.OrderList) Session["ShoppingCart"]);

SubTotal.Text = String.Format("{0:C}", shoppingCart.SubTotal);
Tax.Text = String.Format("{0:C}", shoppingCart.Tax);
Total.Text = String.Format("{0:C}", shoppingCart.Total);

ShoppingCartList.DataSource=shoppingCart.Values;
ShoppingCartList.DataBind();
}
}
}
MainForm類中共有11個方法,19個保護域。其中的19個保護域和前面給出的前端介面圖示的頁面元素相對應,這裡不再贅述。11個方法中MainForm()為構建器,其添加了頁面初始化事件Page_Init(),這是ASP.NET Web表單最先處理的事件,一般進行一些基礎的初始化操作。我們可以看到在Page_Init()中進行了初始化組件InitializeComponent()的操作。Page_Load()事件出現在使用者發出請求後,頁面裝載的時候,在這裡一般可做一些商業邏輯初始化方面的操作,比如資料庫的串連,購物卡的初始化等。我們這裡進行了產品展示UpdateProducts()和購物卡的初始化UpdateShoppingCart()的操作。
其他四個方法分別為產品類別的選擇ProductListing_Select(),購買產品的添加AddBtn_Click(),購物卡的重新計算Recalculate_Click(),購物卡的清除ClearCart_Click()都是通過對ASP.NET控制項的操作來觸發相應的事件完成商業邏輯。上面的代碼已經展示的相當清楚,我們不再贅述。
最後我們要向大家說明的是中介層商務邏輯的組件。它由三個類構成:庫存資料類InventoryDB,訂單項目類OrderItem和訂單列表類OrderList。它們共同在檔案CornfieldGrocer.cs檔案中定義。自解釋的編程方式已經它們的結構展示的相當清除,我們下面只給出該檔案的CS原始碼:

using System;
using System.Data;
using System.Data.SqlClient;
using System.Collections;

namespace CornfieldGrocer
{
public class InventoryDB
{
public DataTable GetProducts(int categoryID)
{
SqlConnection sqlConnect= new SqlConnection
("server=(local);database=CornfieldGrocer;Trusted_Connection=yes");
SqlDataAdapter sqlAdapter1 = new SqlDataAdapter
("Select * from Products where categoryid="+categoryID,sqlConnect);
DataSet products = new DataSet();
sqlAdapter1.Fill(products, "products");
return products.Tables[0];
}
public DataRow GetProduct(int productID)
{
SqlConnection sqlConnect= new SqlConnection
("server=(local);database=CornfieldGrocer;Trusted_Connection=yes");
SqlDataAdapter sqlAdapter1 = new SqlDataAdapter
("Select * from Products where productID=" + productID, sqlConnect);
DataSet product = new DataSet();
sqlAdapter1.Fill(product, "product");
return product.Tables[0].Rows[0];
}
public DataTable GetProductCalories(int productID)
{
SqlConnection sqlConnect = new SqlConnection
("server=(local);database=CornfieldGrocer;Trusted_Connection=yes");
SqlDataAdapter sqlAdapter1 = new SqlDataAdapter
("Select * from Details where productID="+productID,sqlConnect);
DataSet details = new DataSet();
sqlAdapter1.Fill(details, "details");
return details.Tables[0];
}
}

public class OrderItem
{
public int productID;
public int quantity;
public String name;
public double price;
public OrderItem(int productID, String name, double price, int quantity)
{
this.productID = productID;
this.quantity = quantity;
this.name = name;
this.price = price;
}
public int ProductID
{
get { return ProductID; }
}
public int Quantity
{
get { return quantity; }
set { quantity=value; }
}
public String Name
{
get { return name; }
}
public double Price
{
get { return price; }
}
public double Total
{
get { return quantity * price; }
}
}

public class OrderList
{
private Hashtable orders = new Hashtable();
private double taxRate = 0.08;
public double SubTotal
{
get
{
if (orders.Count == 0)
return 0.0;
double subTotal = 0;
IEnumerator items = orders.Values.GetEnumerator();
while(items.MoveNext())
{
subTotal += ((OrderItem) items.Current).Price *
((OrderItem) items.Current).Quantity;
}
return subTotal;
}
}
public double TaxRate
{
get { return taxRate; }
set { taxRate = value; }
}
public double Tax
{
get { return SubTotal * taxRate; }
}
public double Total
{
get { return SubTotal * (1 + taxRate); }
}
public ICollection Values {
get {
return orders.Values;
}
}
public OrderItem this[String name] {
get {
return (OrderItem) orders[name];
}
}
public void Add(OrderItem value)
{
if (orders[value.Name] == null) {
orders.Add(value.Name, value);
}
else
{
OrderItem oI = (OrderItem)orders[value.Name];
oI.Quantity = oI.Quantity + 1;
}
}
public void ClearCart() {
orders.Clear();
}
}
}

需要說明的是我們將三個檔案CornfieldGrocer.cs,Default.aspx.cs,Global.asax.cs用編譯命令“csc /t:library /out:CornfieldGrocer.dll cornfieldgrocer.cs default.aspx.cs global.asax.cs”將它們全部封裝在CornfieldGrocer命名空間裡,雖然這並不是必須的。上面的編譯器輸出CornfieldGrocer.dll檔案,我們配置該網上零售網站時只需將該檔案拷貝到網站根目錄中的Bin目錄下即可。
到此為止,我們已經完整的向大家展示了利用ASP.NET Web表單建立一個小型的網上交易系統的編碼,配置等工作。當然作為示範案例,它還沒有真正系統的完善的效能,安全,介面等各個方面的最佳化考慮和設計。但它向我們展示的ASP.NET Web表單模型卻非常典型且底層,大家不防在此基礎上通過不斷的修改和擴充來開發適合自己的交易系統。比如對於Default.aspx檔案中AutoEventWireup="false"如果設定為“true”或去掉這個語句,在運行頁面時會出現什麼情況?通過這些練習便會不斷的加深我們對ASP.NET底層的認識,最後達到遊刃有餘的把握。實際上技術的學習,尤其是編程,除了一定的興趣和悟性外,大量代碼執行個體的鍛煉也是很有必要的,這本身就是筆者成長的一個過程,也是本文中筆者竭力要給大家展示的。


相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.