浅析PetShop程序中的购物车和订单处理模块(Profile技术,异步MSMQ消息)
?
1. Profile技術(shù)在PetShop程序中用于三處:
??? 1) 購物車ShoppingCart ???-下面的例子圍繞購物車流程進(jìn)行
??? 2) 收藏WishList
??? 3)?用戶信息AccountInfo
??注冊新用戶 NewUser.aspx :使用的是CreateUserWizard 控件,基于MemberShip機(jī)制,在數(shù)據(jù)庫MSPetShop4Services的表aspnet_Users中創(chuàng)建用戶
??修改用戶注冊信息 UserProfile.aspx: 基于Profile技術(shù),在數(shù)據(jù)庫MSPetShop4Profile的表Profiles和Account中創(chuàng)建用戶信息
2. 異步消息處理技術(shù)運(yùn)用于訂單處理
4.1 Web.config配置
Profile可以利用數(shù)據(jù)庫存儲關(guān)于用戶的個性化信息,有點(diǎn)象session對象,但session對象是有生存期的,在生存期后,session對象自動失效了。而profile不同,除非顯式移除它。要實(shí)現(xiàn)profile功能,必須先在web.config中進(jìn)行定義。
在web.congfig中,將會定義一些屬性/值,分別存貯將要保存的變量和值,比如language屬性,定義其值是string類型,如此類推。而<group>標(biāo)簽,則是將一些相同或類似功能的變量值放在一起。
程序中使用方法: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技術(shù)
1.?????? 點(diǎn)擊“加入購物車”: 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 方法將修改后的配置文件屬性值寫入到數(shù)據(jù)源,如ShoppingCart屬性已經(jīng)改變
??????????????? Profile.Save();???
?????????
??????????????? // Redirect to prevent duplictations in the cart if user hits "Refresh"
??????????????? //防止刷新造成 多次提交
??????????????? Response.Redirect("~/ShoppingCart.aspx", true);?//將客戶端重定向到新的 URL。指定新的 URL 并指定當(dāng)前頁的執(zhí)行是否應(yīng)終止。
??????????? }
??????? }
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;
//獲取與指定的鍵相關(guān)聯(lián)的值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 方法將修改后的配置文件屬性值寫入到數(shù)據(jù)源,如ShoppingCart屬性已經(jīng)改變
??????????????? Profile.Save();?
如何更新:
??? 根據(jù)配置中的ShoppingCartProvider類型 PetShop.Profile.PetShopProfileProvider。
?
ASP.NET 配置文件提供對用戶特定屬性的持久性存儲和檢索。配置文件屬性值和信息按照由 ProfileProvider 實(shí)現(xiàn)確定的方式存儲在數(shù)據(jù)源中。
每個用戶配置文件在數(shù)據(jù)庫的 Profiles 表中進(jìn)行唯一標(biāo)識。該表包含配置文件信息,如應(yīng)用程序名稱和上次活動日期。
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
// 創(chuàng)建 PetShop.SQLProfileDAL.PetShopProfileProvider類-數(shù)據(jù)庫操作
???????? private static readonly IPetShopProfileProvider dal
= DataAccess.CreatePetShopProfileProvider();
?
/// <summary>
??????? /// 設(shè)置指定的屬性設(shè)置組的值
??????? /// </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類
使用事務(wù):包含兩個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 訂單處理技術(shù)
訂單處理技術(shù):――分布式事務(wù)
1)?同步:直接在事務(wù)中 將訂單 插入到數(shù)據(jù)庫中,同時(shí)更新庫存
2)?異步:訂單-》消息隊(duì)列(使用MSMQ)-》后臺處理?
4.3.1 使用Wizard組件
4.3.2 分布式事務(wù)處理技術(shù)
開啟MSDTC 服務(wù)支持分布式事務(wù). To start the MSDTC service, open Administrative Tools | Services and start the Distributed Transaction Coordinator service
4.3.3 MSMQ 消息隊(duì)列簡介
1)引用隊(duì)列
????? 引用隊(duì)列有三種方法,通過路徑、格式名和標(biāo)簽引用隊(duì)列,這里我只介紹最簡單和最常用的方法:通過路徑引用隊(duì)列。隊(duì)列路徑的形式為 machinename\queuename。指向隊(duì)列的路徑總是唯一的。下表列出用于每種類型的隊(duì)列的路徑信息:
如果是發(fā)送到本機(jī)上,還可以使用”.”代表本機(jī)名稱。
2)消息的創(chuàng)建
不過要使用MSMQ開發(fā)你的消息處理程序,必須在開發(fā)系統(tǒng)和使用程序的主機(jī)上安裝消息隊(duì)列。消息隊(duì)列的安裝屬于Windows組件的安裝,和一般的組件安裝方法類似。
往系統(tǒng)中添加隊(duì)列十分的簡單,打開[控制面板]中的[計(jì)算機(jī)管理],展開[服務(wù)和應(yīng)用程序],找到并展開[消息隊(duì)列](如果找不到,說明你還沒有安裝消息隊(duì)列,安裝windows組件),右擊希望添加的消息隊(duì)列的類別,選擇新建隊(duì)列即可。
消息接收服務(wù)位于System.Messaging中,在初始化時(shí)引用消息隊(duì)列的代碼很簡單,如下所示:
MessageQueue Mq=new MessageQueue(“.\\private$\\jiang”);
通過Path屬性引用消息隊(duì)列的代碼也十分簡單:
MessageQueue Mq=new MessageQueue();
Mq.Path=”.\\private$\\jiang”;
使用Create 方法可以在計(jì)算機(jī)上創(chuàng)建隊(duì)列:
System.Messaging.MessageQueue.Create(@".\private$\jiang");
3) 發(fā)送和接收消息
過程:消息的創(chuàng)建-》發(fā)送-》接收-》閱讀-》關(guān)閉
簡單消息的發(fā)送示例如下:
???????? Mq.Send(1000); //發(fā)送整型數(shù)據(jù)
???????? Mq.Send(“This is a test message!”); //發(fā)送字符串
接收消息由兩種方式:通過Receive方法接收消息同時(shí)永久性地從隊(duì)列中刪除消息;通過Peek方法從隊(duì)列中取出消息而不從隊(duì)列中移除該消息。如果知道消息的標(biāo)識符(ID),還可以通過ReceiveById方法和PeekById方法完成相應(yīng)的操作。
???? 接收消息的代碼很簡單:
???????? Mq.Receive(); //或Mq.ReceiveById(ID);
???????? Mq.Peek();?// 或Mq.PeekById(ID);
閱讀消息
接收到的消息只有能夠讀出來才是有用的消息,因此接收到消息以后還必須能讀出消息,而讀出消息算是最復(fù)雜的一部操作了。消息的序列化可以通過Visual Studio 和 .NET Framework 附帶的三個預(yù)定義的格式化程序來完成:XMLMessageFormatter 對象( MessageQueue 組件的默認(rèn)格式化程序設(shè)置)、BinaryMessageFormatter 對象、ActiveXMessageFormatter 對象。由于后兩者格式化后的消息通常不能為人閱讀,所以我們經(jīng)常用到的是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);
關(guān)閉消息隊(duì)列
???? 消息隊(duì)列的關(guān)閉很簡單,和其他對象一樣,通過Close函數(shù)就可以實(shí)現(xiàn)了:
Mq.Close();
4.3.4 PetShop程序中訂單處理-使用同步消息
默認(rèn)程序使用同步消息 處理,直接操作數(shù)據(jù)庫插入訂單,更新庫存類
4.3.5 PetShop程序中訂單處理-使用異步消息
1)??? Web程序中調(diào)用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()?-消息隊(duì)列
??????? private static readonly PetShop.IMessaging.IOrder orderQueue
= PetShop.MessagingFactory.QueueAccess.CreateOrder();
?
public void Insert(OrderInfo order) {
??????????? // Call credit card procesor,采用隨機(jī)化方法設(shè)置訂單認(rèn)證數(shù)字
??????????? ProcessCreditCard(order);
??????????? // Insert the order (a)synchrounously based on configuration
??????????? orderInsertStrategy.Insert(order);??? //調(diào)用PetShop.BLL.OrderAsynchronous類
??????? }
3)??? PetShop.BLL. OrderAsynchronous類
?
// CreateOrder()方法得到PetShop.MSMQMessaging .Order類的實(shí)例
private static readonly PetShop.IMessaging.IOrder asynchOrder
?= PetShop.MessagingFactory.QueueAccess.CreateOrder();
public void Insert(PetShop.Model.OrderInfo order) {
??????????? asynchOrder.Send(order);??? //調(diào)用PetShop.MSMQMessaging.Order類
??????? }
4)??? PetShop.MSMQMessaging項(xiàng)目 -關(guān)鍵(發(fā)送/接收消息)
PetShopQueue基類:創(chuàng)建消息隊(duì)列,發(fā)送和接收消息
Order類:繼承自PetShopQueue類
public new OrderInfo Receive() {??????????? //從隊(duì)列中接收消息
??????????? base.transactionType = MessageQueueTransactionType.Automatic;
??????????? return (OrderInfo)((Message)base.Receive()).Body;
??????? }
public void Send(OrderInfo orderMessage) {?//發(fā)送消息到隊(duì)列
??????????? base.transactionType = MessageQueueTransactionType.Single;
??????????? base.Send(orderMessage);
??????? }
5)??? PetShop.OrderProcessor項(xiàng)目-后臺處理訂單,將它們插入到數(shù)據(jù)庫中
Program類:多線程后臺訂單處理程序,可寫成一個控制臺程序,作為windows服務(wù)開啟
處理隊(duì)列中的批量異步訂單,在事務(wù)范圍內(nèi)把它們提交到數(shù)據(jù)庫
轉(zhuǎn)載于:https://www.cnblogs.com/hnsjack/articles/608501.html
總結(jié)
以上是生活随笔為你收集整理的浅析PetShop程序中的购物车和订单处理模块(Profile技术,异步MSMQ消息)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: leetcode
- 下一篇: ExtJs6 Desktop Demo