浅析Microsoft .net PetShop程序中的购物车和订单处理模块(Profile技术,异步MSMQ消息)

  • Post author:
  • Post category:其他









对于Microsoft .net PetShop程序中的购物车和订单处理模块,文中主要分析两种技术的应用:







1. Profile



技术在



PetShop



程序中用于三处:




1)







购物车



ShoppingCart






-下面的例子围绕购物车流程进行





2)







收藏


WishList




3)







用户信息



AccountInfo











注册新用户

NewUser.aspx :




使用的是

CreateUserWizard

控件,基于

MemberShip机制,在数据库




MSPetShop4Services


的表

aspnet_Users

中创建用户













修改用户注册信息

UserProfile.aspx:




基于

Profile技术,在数据库




MSPetShop4Profile的表Profiles和Account中创建用户信息





2. 异步消息处理技术运用于订单处理









4.1 Web.config


配置



Profile可以利用数据库存储关于用户的个性化信息,有点象session对象,但session对象是有生存期的,在生存期后,session对象自动失效了。而profile不同,除非显式移除它。要实现profile功能,必须先在web.config中进行定义。





web.congfig中,将会定义一些属性/值,分别存贮将要保存的变量和值,比如language属性,定义其值是string类型,如此类推。而<group>标签,则是将一些相同或类似功能的变量值放在一起。




程序中使用方法:

Profile.language = ddlLanguage.SelectedItem.Value;






<


profile




automaticSaveEnabled


=




false





defaultProvider


=



ShoppingCartProvider



>




<


providers


>




<


add




name


=




ShoppingCartProvider





connectionStringName


=



SQLProfileConnString





type


=



PetShop.Profile.PetShopProfileProvider





applicationName


=



.NET Pet Shop 4.0



/>




<


add




name


=




WishListProvider





connectionStringName


=



SQLProfileConnString





type


=



PetShop.Profile.PetShopProfileProvider





applicationName


=



.NET Pet Shop 4.0



/>




<


add




name


=




AccountInfoProvider





connectionStringName


=



SQLProfileConnString





type


=



PetShop.Profile.PetShopProfileProvider





applicationName


=



.NET Pet Shop 4.0



/>




</


providers


>




<


properties


>




<


add




name


=





ShoppingCart






type


=



PetShop.BLL.

Cart






allowAnonymous


=



true





provider


=



ShoppingCartProvider



/>




<


add




name


=




WishList





type


=



PetShop.BLL.Cart





allowAnonymous


=



true





provider


=



WishListProvider



/>




<


add




name


=




AccountInfo





type


=



PetShop.Model.AddressInfo





allowAnonymous


=



false





provider


=



AccountInfoProvider



/>




</


properties


>




</


profile


>


4.2 购物车


程序流程-


Profile


技术






1.







点击“加入购物车”:




http://localhost:2327/Web/ShoppingCart.aspx?addItem=EST-34





2.






ShoppingCart.aspx文件处理:在init方法之前处理








protected



void

Page_PreInit(

object

sender,

EventArgs

e) {





if

(!IsPostBack) {





string

itemId = Request.QueryString[

“addItem”

];





if

(!

string

.IsNullOrEmpty(itemId)) {





Profile.ShoppingCart.Add(itemId);

//

注意ShoppingCart的类型是PetShop.BLL.Cart






//Save 方法将修改后的配置文件属性值写入到数据源,如ShoppingCart属性已经改变






Profile.Save();














// Redirect to prevent duplictations in the cart if user hits “Refresh”




//防止刷新造成 多次提交




Response.Redirect(

“~/ShoppingCart.aspx”

,

true

);


//



将客户端重定向到新的



URL



。指定新的



URL



并指定当前页的执行是否应终止。






}




}




3.





PetShop.BLL.Cart类



//


Dictionary: key/value





private



Dictionary

<

string

,

CartItemInfo

>

cartItems

=

new


Dictionary

<

string

,

CartItemInfo

>();



///




<summary>





///


Add an item to the cart.





///


When ItemId to be added has already existed, this method will update the quantity instead.





///




</summary>





///




<param name=”itemId”>


Item Id of item to add


</param>





public


void

Add(

string

itemId) {


CartItemInfo


cartItem;


//获取与指定的键相关联的值TryGetValue(TKey key,out TValue value)






if

(!cartItems.TryGetValue(itemId,

out

cartItem)) {





Item

item =

new


Item

();





ItemInfo

data = item.GetItem(itemId);





if

(data !=

null

) {







CartItemInfo

newItem =

new


CartItemInfo

(itemId, data.ProductName, 1, (

decimal

)data.Price, data.Name, data.CategoryId, data.ProductId);




cartItems.Add(itemId, newItem);




}




}





else






cartItem.Quantity++;




}






4.






更新

Profile





//Save 方法将修改后的配置文件属性值写入到数据源,如ShoppingCart属性已经改变






Profile.Save();





如何更新

:






根据配置中的



ShoppingCartProvider


类型

PetShop.Profile.PetShopProfileProvider。





ASP.NET



配置文件提供对用户特定属性的持久性存储和检索。配置文件属性值和信息按照由



ProfileProvider



实现确定的方式存储在数据源中。



每个用户配置文件在数据库的



Profiles



表中进行唯一标识。该表包含配置文件信息,如应用程序名称和上次活动日期。

CREATE TABLE Profiles
  
  
(
  
  
  UniqueID AutoIncrement NOT NULL PRIMARY KEY,
  
  
  Username Text (255) NOT NULL,
  
  
  ApplicationName Text (255) NOT NULL,
  
  
  IsAnonymous YesNo, 
  
  
  LastActivityDate DateTime,
  
  
  LastUpdatedDate DateTime,
  
  
  CONSTRAINT PKProfiles UNIQUE (Username, ApplicationName)
  
  
)
  
  






5.






PetShop.Profile. PetShopProfileProvider类, 继承自ProfileProvider




// 创建 PetShop.SQLProfileDAL.PetShopProfileProvider类-数据库操作





private


static


readonly


IPetShopProfileProvider

dal


=

DataAccess

.CreatePetShopProfileProvider();



///




<summary>





///


设置指定的属性设置组的值





///




</summary>





public


override


void

SetPropertyValues(

SettingsContext

context,

SettingsPropertyValueCollection

collection) {





string

username = (

string

)context[

“UserName”

];




CheckUserName(username);








bool

isAuthenticated = (

bool

)context[

“IsAuthenticated”

];






int

uniqueID = dal.GetUniqueID(username, isAuthenticated,

false

, ApplicationName);





if

(uniqueID == 0)




uniqueID = dal.CreateProfileForUser(username, isAuthenticated, ApplicationName);






foreach

(

SettingsPropertyValue

pv

in

collection) {





if

(pv.PropertyValue !=

null

) {





switch

(pv.Property.Name) {





case

PROFILE_SHOPPINGCART:


//

ShoppingCart




SetCartItems(uniqueID, (

Cart

)pv.PropertyValue,

true

);





break

;





case

PROFILE_WISHLIST:




SetCartItems(uniqueID, (

Cart

)pv.PropertyValue,

false

);





break

;





case

PROFILE_ACCOUNT:





if

(isAuthenticated)




SetAccountInfo(uniqueID, (

AddressInfo

)pv.PropertyValue);





break

;





default

:





throw


new


ApplicationException

(ERR_INVALID_PARAMETER +

” name.”

);




}




}




}





UpdateActivityDates(username,

false

);




}



// Update cart





private


static


void

SetCartItems(

int

uniqueID,

Cart

cart,

bool

isShoppingCart) {




dal.SetCartItems(uniqueID, cart.CartItems, isShoppingCart);




}







6.







PetShop.SQLProfileDAL. PetShopProfileProvider类



使用事务:包含两个

sql动作,先删除,再插入





///




<summary>





///


Update shopping cart for current user





///




</summary>





///




<param name=”uniqueID”>


User id


</param>





///




<param name=”cartItems”>


Collection of shopping cart items


</param>





///




<param name=”isShoppingCart”>


Shopping cart flag


</param>





public


void

SetCartItems(

int

uniqueID,

ICollection

<

CartItemInfo

> cartItems,

bool

isShoppingCart) {





string

sqlDelete =

“DELETE FROM Cart WHERE UniqueID = @UniqueID AND IsShoppingCart = @IsShoppingCart;”

;






SqlParameter

[] parms1 = {









new


SqlParameter

(

“@UniqueID”

,

SqlDbType

.Int),





new


SqlParameter

(

“@IsShoppingCart”

,

SqlDbType

.Bit)};




parms1[0].Value = uniqueID;




parms1[1].Value = isShoppingCart;






if

(cartItems.Count > 0) {






// update cart using SqlTransaction





string

sqlInsert =

“INSERT INTO Cart (UniqueID, ItemId, Name, Type, Price, CategoryId, ProductId, IsShoppingCart, Quantity) VALUES (@UniqueID, @ItemId, @Name, @Type, @Price, @CategoryId, @ProductId, @IsShoppingCart, @Quantity);”

;






SqlParameter

[] parms2 = {









new


SqlParameter

(

“@UniqueID”

,

SqlDbType

.Int),






new


SqlParameter

(

“@IsShoppingCart”

,

SqlDbType

.Bit),





new


SqlParameter

(

“@ItemId”

,

SqlDbType

.VarChar, 10),





new


SqlParameter

(

“@Name”

,

SqlDbType

.VarChar, 80),





new


SqlParameter

(

“@Type”

,

SqlDbType

.VarChar, 80),





new


SqlParameter

(

“@Price”

,

SqlDbType

.Decimal, 8),





new


SqlParameter

(

“@CategoryId”

,

SqlDbType

.VarChar, 10),





new


SqlParameter

(

“@ProductId”

,

SqlDbType

.VarChar, 10),





new


SqlParameter

(

“@Quantity”

,

SqlDbType

.Int)};




parms2[0].Value = uniqueID;






parms2[1].Value = isShoppingCart;






SqlConnection

conn =

new


SqlConnection

(

SqlHelper

.ConnectionStringProfile);




conn.Open();





SqlTransaction

trans = conn.BeginTransaction(

IsolationLevel

.ReadCommitted);








try

{





SqlHelper

.ExecuteNonQuery(trans,

CommandType

.Text, sqlDelete, parms1);






foreach

(

CartItemInfo

cartItem

in

cartItems) {




parms2[2].Value = cartItem.ItemId;






parms2[3].Value = cartItem.Name;




parms2[4].Value = cartItem.Type;




parms2[5].Value = cartItem.Price;




parms2[6].Value = cartItem.CategoryId;




parms2[7].Value = cartItem.ProductId;




parms2[8].Value = cartItem.Quantity;





SqlHelper

.ExecuteNonQuery(trans,

CommandType

.Text, sqlInsert, parms2);




}




trans.Commit();




}







catch

(

Exception

e) {




trans.Rollback();





throw


new


ApplicationException

(e.Message);




}





finally

{




conn.Close();




}




}







else





// delete cart





SqlHelper

.ExecuteNonQuery(

SqlHelper

.ConnectionStringProfile,

CommandType

.Text, sqlDelete, parms1);




}


4.3


订单处理技术













订单处理技术:――分布式事务






1)







同步:直接在事务中






将订单






插入到数据库中,同时更新库存






2)







异步:订单-》消息队列



(



使用



MSMQ)



-》后台处理










4.3.1



使用



Wizard



组件





4.3.2



分布式事务处理技术




开启



MSDTC



服务支持分布式事务



. To start the MSDTC service, open Administrative Tools | Services and start the Distributed Transaction Coordinator service





4.3.3 MSMQ



消息队列简介










1



)引用队列
















引用队列有三种方法,通过路径、格式名和标签引用队列,这里我只介绍最简单和最常用的方法:

通过路径



用队列

。队列路径的形式为



machinename/queuename



。指向队列的路径总是唯一的。下表列出用于每种类型的队列的路径信息:






如果是发送到本机上,还可以使用”



.



”代表本机名称。











2



)消息的创建





不过要使用



MSMQ



开发你的消息处理程序,必须在开发系统和使用程序的主机上安装消息队列。消息队列的安装属于



Windows



组件的安装,和一般的组件安装方法类似。



往系统中添加队列十分的简单,打开



[



控制面板



]



中的



[



计算机管理



]



,展开



[



服务和应用程序



]



,找到并展开



[



消息队列



]



(如果找不到,说明你还没有安装消息队列,安装



windows



组件),右击希望添加的消息队列的类别,选择新建队列即可。








消息接收服务位于



System.Messaging



中,在初始化时引用消息队列的代码很简单,如下所示:




MessageQueue Mq=new MessageQueue(







.//private$//jiang







)












通过



Path



属性引用消息队列的代码也十分简单:




MessageQueue Mq=new MessageQueue()







Mq.Path=”.//private$//jiang”;








使用



Create



方法可以在计算机上创建队列:



System.Messaging.MessageQueue.Create(@”./private$/jiang”);









3)



发送和接收消息





过程:消息的创建-》发送-》接收-》阅读-》关闭



简单消息的发送示例如下:






Mq.Send(1000); //



发送整型数据






Mq.Send(







This is a test message!







); //



发送字符串








接收消息由两种方式:通过



Receive



方法接收消息同时永久性地从队列中删除消息;通过



Peek



方法从队列中取出消息而不从队列中移除该消息。如果知道消息的标识符(



ID



),还可以通过



ReceiveById



方法和



PeekById



方法完成相应的操作。









接收消息的代码很简单:






Mq.Receive(); //







Mq.ReceiveById(ID);







Mq.Peek();


//







Mq.PeekById(ID);









阅读消息



接收到的消息只有能够读出来才是有用的消息,因此接收到消息以后还必须能读出消息,而读出消息算是最复杂的一部操作了。消息的序列化可以通过



Visual Studio







.NET Framework



附带的三个预定义的格式化程序来完成:



XMLMessageFormatter



对象(



MessageQueue



组件的默认格式化程序设置)、



BinaryMessageFormatter



对象、



ActiveXMessageFormatter



对象。由于后两者格式化后的消息通常不能为人阅读,所以我们经常用到的是



XMLMessageFormatter



对象。



使用



XMLMessageFormatter



对象格式化消息的代码如下所示:






string[] types = { “System.String” };







((XmlMessageFormatter)mq.Formatter).TargetTypeNames = types;







Message m=mq.Receive(new TimeSpan(0,0,3));










将接收到的消息传送给消息变量以后,通过消息变量



m







Body



属性就可以读出消息了:



MessageBox.Show((string)m.Body);








关闭消息队列









消息队列的关闭很简单,和其他对象一样,通过



Close



函数就可以实现了:



Mq.Close();









4.3.4 PetShop



程序中订单处理-使用同步消息




默认程序使用同步消息 处理,直接操作数据库插入订单,更新库存类








4.3.5 PetShop



程序中订单处理-使用异步消息







1)








Web程序中调用



PetShop.BLL.Order类方法:


Insert(OrderInfo order);







2)








PetShop.BLL.Order类







//IOrderStrategy接口中只有一个插入订单方法:void Insert(PetShop.Model.OrderInfo order);





//得到PetShop.BLL. OrderAsynchronous类





private


static


readonly

PetShop.IBLLStrategy.

IOrderStrategy

orderInsertStrategy = LoadInsertStrategy();








//IOrder接口中有两种方法:Send()与Receive()


-消息队列





private


static


readonly

PetShop.IMessaging.

IOrder

orderQueue


= PetShop.MessagingFactory.

QueueAccess

.CreateOrder();



public



void

Insert(

OrderInfo

order) {





// Call credit card procesor,采用随机化方法设置订单认证数字




ProcessCreditCard(order);





// Insert the order (a)synchrounously based on configuration




orderInsertStrategy.Insert(order);


//调用

PetShop.BLL.




OrderAsynchronous









}









3)







PetShop.BLL. OrderAsynchronous类





//


CreateOrder()方法得到

PetShop.MSMQMessaging


.Order类的实例




private



static


readonly

PetShop.IMessaging.

IOrder

asynchOrder




= PetShop.MessagingFactory.

QueueAccess

.CreateOrder();



public



void

Insert(PetShop.Model.

OrderInfo

order) {




asynchOrder.Send(order);


//调用

PetShop.MSMQMessaging


.Order类




}









4)








PetShop.MSMQMessaging项目 -关键(发送/接收消息)














PetShopQueue基类:



创建消息队列,发送和接收消息








Order类:继承自PetShopQueue类



public



new


OrderInfo

Receive() {



//从队列中接收消息






base

.transactionType =

MessageQueueTransactionType

.Automatic;





return

(

OrderInfo

)((

Message

)

base

.Receive()).Body;




}


public



void

Send(

OrderInfo

orderMessage) {



//发送消息到队列





base

.transactionType =

MessageQueueTransactionType

.Single;





base

.Send(orderMessage);




}












5)








PetShop.




OrderProcessor项目-后台处理订单,将它们插入到数据库中



Program类:


多线程后台订单处理程序,可写成一个控制台程序,作为

windows服务开启






处理队列中的批量异步订单,在事务范围内把它们提交到数据库




版权声明:本文为wangyihust原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。