淺析Microsoft .net PetShop程序中的購物車和訂單處理模塊(Profile技術,異步MSMQ消息)

 

對於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服務開啓
處理隊列中的批量異步訂單,在事務範圍內把它們提交到數據庫
發佈了43 篇原創文章 · 獲贊 3 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章