学习PetShop3.0(分析)

来源:互联网 发布:淘宝男生手表 编辑:程序博客网 时间:2024/05/17 18:04

 (1)学习PetShop3.0用户注册
关于该系统的大致介绍可以从上面的连接获得,都是中文的。
下面来分析一下PetShop3.0的用户注册部分
PetShop3.0是业务实体和业务逻辑分开的,并且在表示层上也有逻辑处理。业务实体部分从前到后都有用到。实际上,在传递数据的时候就是传递的一个实体,而不是像我们一般用的一个变量一个变量的传,在用户注册中也是这样。
注册页面是CreateAccount.aspx,这里有一个usercontrol:AddressUI,用来收集用户的一般信息,其他的个人网站设定和用户名密码什么的都是分开来取的,通过提取AddressUI.Address来获得一个AddressInfo对象,然后用这些信息创建一个AccountInfo对象,最后调用ProcessFlow.AccountController的CreateAccount方法来完成注册。CreateAccount接收的参数自然是一个AddressInfo类型的对象,返回类型为bool。根据返回值来判断注册是否成功。实际上,它这里假定如果不成功,那就只有一种情况,就是用户名已经被注册了。
接下来的事情就是一层套一层的引用了。把业务实体AccountInfo一层的往下传,最后到达SQLServerDAL

(2) 宠物展示
涉及到这个主题的页有Category.aspx / Items.aspx / ItemDetails.aspx,分别是大类/小类/详细信息这三个。下面来一个一个的分析
要注意的是,像第一篇说的,数据的传递都是采用直接传递业务实体的方法来完成。这样是不是有很强的面向对象的味道?
Category.aspx


(3)模仿购物车的简单可变类
Store类模仿购物车内的物品

CODE:
[Copy to clipboard]
public class Store
{
private string name;
private int id;
private DateTime time;

public Store(string name,int id,DateTime time)
{
this.name=name;
this.id=id;
this.time=time;
}

//属性
public string Name
{
get{return this.name;}
}

public int Id
{
get{return this.id;}
}

public DateTime Time
{
get{return this.time;}
}
}

StoreList类模仿购物车

CODE:
[Copy to clipboard]
public class StoreList : IEnumerable
{
ArrayList al=new ArrayList();
public StoreList()
{}
//向车内添加物品
public void Add(Store st)
{
this.al.Add(st);
}

//返回全部物品
public ArrayList List
{
get{return this.al;}
}
//实现IEnumerable接口
#region IEnumerable 成员

public IEnumerator GetEnumerator()
{
return this.al.GetEnumerator();
}

#endregion

//添加一个索引器,注意没有判断索引数的合法性
public Store this[int index]
{
get{return (Store)al[index];}
}

//物品的数量
public int Count
{
get{return al.Count;}
}
}

最后的演示页面

CODE:
[Copy to clipboard]
public class TestStore : System.Web.UI.Page
{
protected System.Web.UI.WebControls.Button addStore;
protected System.Web.UI.WebControls.Label showMsg;

private void Page_Load(object sender, System.EventArgs e)
{
show();
}

#region Web 窗体设计器生成的代码
override protected void OnInit(EventArgs e)
{
//
// CODEGEN: 该调用是 ASP.NET Web 窗体设计器所必需的。
//
InitializeComponent();
base.OnInit(e);
}

/// <summary>
/// 设计器支持所需的方法 - 不要使用代码编辑器修改
/// 此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.addStore.Click += new System.EventHandler(this.addStore_Click);
this.Load += new System.EventHandler(this.Page_Load);

}
#endregion

//点击添加按钮后的处理事件,向保存在Session中的购物车添加一个商品
private void addStore_Click(object sender, System.EventArgs e)
{
Store st=new Store("alex",0,DateTime.Now);
//检查Session内是否存有购物车,如没有,则添加一个
if(Session["stxx"]==null)
{
StoreList sl=new StoreList();
Session["stxx"]=sl;
}
//从Session中得到购物车,然后向里面添加一个商品
StoreList sls=(StoreList)Session["stxx"];
sls.Add(st);
//注意这里,最后分析这个
//Session["stxx"]=sls;
}

//展示购物车内的商品
private void show()
{
StringBuilder sb=new StringBuilder();
if(Session["stxx"]!=null)
{
StoreList sls=(StoreList)Session["stxx"];
//利用索引循环取出商品
for(int i=0;i<sls.Count;i++)
sb.Append(sls[i].Time.ToString()+"<br>");
showMsg.Text=sb.ToString();
}
}
}

Store是一个瘦实体类,而StoreList是一个可变类。StoreList类通过里面的ArrayList保存Store类,并提供了相应的方法来对Store进行操作。
来看这个:

CODE:
[Copy to clipboard]
//从Session中得到购物车,然后向里面添加一个商品
StoreList sls=(StoreList)Session["stxx"];
sls.Add(st);
//注意这里,最后分析这个
//Session["stxx"]=sls;

这里涉及到一个关于Session的问题,由于我们的StoreList保存在了Session中,所以每次操作都要先从Session里把StoreList取出来,但是在操作完后,并没有再把StoreList保存回Session,这主要是因为我们提取出来的并不是Session里保存的值,而只是得到了对Session里保存的值的引用,所以之后的操作其实都是在对Session里保存的值进行,就没有必要最后再保存了。

(4)购物车
终于到购物车了,在看这个之前应该已经明白了第三篇的那个模型,这样购物车基本也就明白了。
来看一下ShoppingCart.aspx这个页。
当你看好了一个宠物,比如可爱的Golden Retriever ,嘿嘿,那就点add to cart按钮,这时就会跳到ShoppingCart.aspx,url里带了这个宠物的id号,根据该id号程序将该宠物放到cart里面。然后你可以再去挑别的宠物,比如一只猫(……),虽然这不是什么好主意。然后该宠物的id号又会被传到ShoppingCart.aspx,并添加到cart里面。在ShoppingCart.aspx里,你可以更改想要领养的宠物的数量,然后程序会根据你要求的数量来计算所需的钱以及该宠物是否还有剩余。在你做出决定后可以点proceed to checkout进入定单生成的环节。
上面是大体的流程。下面来看.net petshop是怎么实现这个cart的
基本的实现主要是BLL里的Cart和Model里的CartItemInfo,而Web.ProcessFlow的CartControler则负责具体的实现。想一想第三篇里的那个模型,具体到这里,每挑选一个宠物,就有一个CartItemInfo通过CartControler添加到了保存在Session里的Cart里面,最后生成定单的时候就从Session里把Cart的值取出来(CartControler有生成定单的方法,下一篇再说)。
来看一下ShoppingCart.aspx.cs里向Cart添加CartItemInfo的代码

CODE:
[Copy to clipboard]
// Create an instance of the cart controller
ProcessFlow.CartController cartController = new ProcessFlow.CartController();

myCart = cartController.GetCart(true);

if (!Page.IsPostBack){

// Get the itemdId from the query string
string itemId = Request["itemId"];

if (itemId != null){
// Clean the input string
itemId = WebComponents.CleanString.InputText(itemId, 50);
myCart.Add(itemId);
cartController.StoreCart(myCart);

}
}

先看这一句 myCart = cartController.GetCart(true);
GetCart方法用来生成一个Cart,它是先在Session里检查,如Session里没有保存Cart,就生成一个新的,否则就把保存在Session里的Cart取出来,然后使用Add方法把新的宠物加到Cart里。

CODE:
[Copy to clipboard]
public Cart GetCart(bool create){

// Fetch the cart object from session state
Cart myCart = (Cart)HttpContext.Current.Session[CART_KEY];

if ( myCart == null ){
if (create){
myCart = new Cart();
}else{
HttpContext.Current.Server.Transfer(URL_NOCART);
return null;
}
}

return myCart;
}

下面是Add方法的实现

CODE:
[Copy to clipboard]
public void Add(string ItemId) {
// _items是在Cart里保存的宠物集合,通过遍历来判断这是否是一个新类别
foreach (CartItemInfo cartItem in _items) {
if (ItemId == cartItem.ItemId) {
cartItem.Quantity++;
cartItem.InStock = (GetInStock(ItemId) - cartItem.Quantity) >= 0 ? true : false;
_total = _total+(cartItem.Price*cartItem.Quantity);
return;
}
}

//是一个新类别,则把一个CartItemInfo加入Cart,可以在这里看到CartItemInfo的组成
Item item = new Item();

ItemInfo data = item.GetItem(ItemId);
CartItemInfo newItem = new CartItemInfo(ItemId,data.Name, (data.Quantity >= 1), 1, (decimal)data.Price);
_items.Add(newItem);
_total = _total+(data.Price);
}

这就完成了添加。然后是数量的更改。

CODE:
[Copy to clipboard]
// Check for update button
if (e.CommandName == CMD_UPDATE){
TextBox txt;
int qty;
int index;

// Go through each item on the page
for (int i = 0, j = cart.Items.Count; i < j; i++){

// lookup the control
txt = (TextBox)cart.Items[i].FindControl(ID_TXT);

try{
qty = int.Parse(txt.Text);
index = cart.CurrentPageIndex * cart.PageSize + i;

// If the new qty is zero, remove the item from the cart
if (qty <= 0)
myCart.RemoveAt(index);
// Update the item with the new quantity
else
myCart[index].Quantity = qty;
}
catch {}
}

}else
// otherwise the command is to remove the an item
myCart.Remove((string)e.CommandArgument);

因为宠物的添加是先于数量的更改进行的,所以到了这里Session里肯定有保存Cart。数量的更改直接同过索引器来完成,更改后直接就会保存。Remove和RemoveAt都实现了从Cart删除指定的CartItemInfo。

ShoppingCart.aspx页还有其他的东西,比如一个继承自SimplePager的自定义控件,,还有判断是否显示用户喜好的宠物列表的判断。
Refresh方法用来重新计算total的值,哈,不过我不清楚微软究竟想拿这个值显示什么?在我下的这个版本里,根本就不是subtotla的总和,而是price的总和,但问题在于,当你把一种宠物从Cart里移除的时候,它竟然会total=total-subtotal,因此常常会出现负数……

购物车从开始到最后销毁,都是在和Session打交道,没有任何与数据库的交互。我不是很了解面向对象技术,但我觉得oo在这里得到了很好的体现。


(5)生成定单
点proceed to checkout后,就进入Checkout.aspx,确认后进入OrderBilling.aspx,在这里可以修改你的信息,完成后点continue,会出现个人信息的只读页面,最终确认后就进入OrderProcess.aspx,在这里是定单的详细情况,并且是只读的,到这里,定单被添加到数据库,购物完成。
Checkout.aspx把数据从Session中取出来,然后显示到页面,没什么好说的。
OrderBilling.aspx,这个页面一开始显示的信息可写,我们看OnLoad事件中,是用ProcessFlow.AccountController.GetAccountInfo获得用户的信息CreditCardInfo,然后显示在一些可写的控件中,比如用户控件:StaticAddress。当点击确认后,使用StoreCreditCard把刚才获得的用户信息保存到Sessin,准备呆会用。
OrderProcess.aspx是最终的定单生成页面。主要就是一个方法:
ProcessFlow.CartController.PurchaseCart 来看它的实现

CODE:
[Copy to clipboard]
public OrderInfo PurchaseCart(){

// Fetch the cart from session
Cart myCart = (Cart)HttpContext.Current.Session[CART_KEY];

// Make some checks on the cart
if ( ( myCart == null ) || ( myCart.Count==0 ) ) {

HttpContext.Current.Server.Transfer(URL_NOCART);
//HttpContext.Current.Response.Redirect(URL_NOCART, false);
return null;

}else{

// Build up the order
OrderInfo newOrder = new OrderInfo();
newOrder.UserId = ((AccountInfo)HttpContext.Current.Session[ACCOUNT_KEY]).UserId;
newOrder.CreditCard = (CreditCardInfo)HttpContext.Current.Session[CREDITCARD_KEY];
newOrder.BillingAddress = (AddressInfo)HttpContext.Current.Session[BILLING_KEY];
newOrder.ShippingAddress = (AddressInfo)HttpContext.Current.Session[SHIPPING_KEY];

newOrder.LineItems = (LineItemInfo[])myCart.GetOrderLineItems().ToArray(typeof(LineItemInfo));

newOrder.OrderTotal = myCart.Total;
newOrder.Date = DateTime.Now;

// Send the order to the middle tier
OrderInsert order = new OrderInsert();
//向数据库插入数据
newOrder.OrderId = order.Insert(newOrder);

// clear the session objects used
HttpContext.Current.Session[CART_KEY] = null;
HttpContext.Current.Session[CREDITCARD_KEY] = null;
HttpContext.Current.Session[BILLING_KEY] = null;
HttpContext.Current.Session[SHIPPING_KEY] = null;

return newOrder;
}
}

Order主要是由保存在Session里的值形成,其中包括购物车。
在Order有一个LineItemInfo,它是由Cart.GetOrderLineItems方法依据cart里CartItemInfo的值返回的,是定单的物品部分,和CartItemInfo相比,主要是多了一个Line属性,这个Line是用来表示物品在定单内的序号。
下面是Insert的实现。

CODE:
[Copy to clipboard]
public int Insert(OrderInfo order) {

// Get an instance of the Order DAL using the DALFactory
IOrder dal = PetShop.DALFactory.Order.Create();

// Call the insert method in the DAL to insert the header
int orderId = dal.Insert(order);

// Get an instance of the Inventory business component
Inventory inventory = new Inventory();
//向数据库插入库存信息
inventory.TakeStock( order.LineItems);

// As part of the sample application we have created a user
// you can tested distributed transactions with
// If the order has been created with the user 'Acid',
// then throw an exception which will rollback the entire transaction
if (order.UserId == ACID_USER_ID)
throw new ApplicationException(ACID_ERROR_MSG);

// Set the orderId so that it can be returned to the caller
return orderId;
}

大体就这么多了,表示层的东西到这里基本上就算全看完了,嘿嘿


(6)实体模型
还记得用户注册时收集信息的方式吗?下面这几句

(7)SimplePager
看一下SQLServerDAL,前面有高人说了,那个SQLHelper类似于daab for .net,因此在这里我就不多话了,对daab for .net的讨论可以参看msdn的相关文章。
来看Account.cs,里面有一个GetAddress方法,用来通过用户名获得用户资料。在这里面主要是分两步,第一步是获得参数并给其赋值,第二步是使用SqlDataReader来从数据库取值。
public AddressInfo GetAddress(string userId) {
AddressInfo address= null;


原创粉丝点击