[*转*] 开发B2C电子商务系统(ASP.NET)--多年前的老文章
開發BtoC電子商務系統(ASP.NET)
??? 原文還有兩個圖
??? 在對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處理后的第一站。
下面為其內容:
<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.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.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.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底層的認識,最后達到游刃有余的把握。實際上技術的學習,尤其是編程,除了一定的興趣和悟性外,大量代碼實例的鍛煉也是很有必要的,這本身就是筆者成長的一個過程,也是本文中筆者竭力要給大家展示的。
轉載于:https://www.cnblogs.com/temptation/archive/2007/06/17/786805.html
總結
以上是生活随笔為你收集整理的[*转*] 开发B2C电子商务系统(ASP.NET)--多年前的老文章的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [导入]mootools框架【三】-Ar
- 下一篇: 用Javascript实现interfa