Microsoft .NET Pet Shop 4 Migrating an ASP.NET 1.1 Application to 2.0

The goals of the .NET Pet Shop  4  project are:

   
1 . Productivity: Reduce the amount of code from .NET Pet Shop  3 —we achieved nearly  25  percent reduction.
   
2 . Migrate from ASP.NET  1.1  to  2.0 : Leverage  new  features of ASP.NET  2.0 —we took advantage of Master Pages, Membership, and Profile and designed a  new , appealing user  interface .

   
2 .

      Figure 
1 . The .NET PetShop  4.0
   
3 . Enterprise architecture: Build a flexible, best practice application—we implemented design patterns and separation of presentation, business, and data tiers.

Productivity

When compared to .NET Pet Shop 
3 , there  is  roughly  25  percent less code  in  .NET Pet Shop  4 . The main gains  in  reducing lines of code are  in  the presentation layer and data access layer.

In the presentation layer, we reduced the code by roughly 
25  percent. The sign - in  and check - out  steps are more compact than full ASP.NET pages and require less code and html. This  is  because the Wizard control natively handles the process flow code. Using the Master Page meant less html code and user controls to manage layout. Membership services handles authentication more succinctly than the Pet Shop  3  user management code.

We saw the biggest code savings 
in  the data tier— 36  percent. The account management code  is  replaced by the ASP.NET  2.0  SQL Membership Provider.

Table 
1  gives a complete code count  break  down by tier.

Table 
1 . Code count comparison  for  .NET Pet Shop Version  3  versus Version  4
      v3     v4
Presentation Layer     
1 , 822       1 , 365
Model     
349       395
Business Logic Layer     
210       199
Data Access Layer     
1 , 538       985
Total Lines of Code     
3 , 919       2 , 944

This 
is  further illustrated  in  the Figure  2 .


Figure 
2 . Code Count Comparison Graph

.NET Pet Shop 
4  introduces several  new  features, ranging from a custom ASP.NET  2.0  profile provider to asynchronous order processing with MSMQ. The code count  for  the  new  features  is  broken down  in  table  2 :

Table 
2 . Code Count of New.NET Pet Shop  4  Features
Custom Profile     
853
Oracle Membership     
586
Cache Dependency     
90
Message Queue     
147
Total Lines of Code     
1 , 676
Migration from ASP.NET 
1.1  to  2.0

To accomplish the goals 
for  .NET Pet Shop  4 , we devised the following plan:

   
1 . Use the Project Conversion Wizard to port the .NET Pet Shop  3.2  code  base  from ASP.NET  1.1  to ASP.NET  2.0 .
   
2 . Map  out  the ASP.NET  2.0  features that we want to include.
   
3 . Implement an n - tier architecture that supports those features.

The Project Conversion Wizard

To start off, the Visual Studio.NET 
2005  Project Conversion Wizard rapidly upgraded the .NET Pet Shop  3.2  code  base . With  this  basic port we were able  get  a first glimpse at .NET Pet Shop  3.2  compiled and running on ASP.NET  2.0 .
Changes Between Version 
3  and Version  4

Based on moving the .NET Pet Shop 
3.2  code  base  to run on the .NET Framework  2.0  and our research into ASP.NET  2.0 , we came up with the following key features to implement  in  .NET Pet Shop  4.0 :

    
*  System.Transactions instead of Serviced Components.
    
*  Generics  for  strongly typed collections instead of loosely typed ILists.
    
*  ASP.NET  2.0  Membership  for  user authentication and authorization.
    
*  Custom ASP.NET  2.0  Membership Provider  for  Oracle 10g.
    
*  ASP.NET  2.0  Custom Oracle and SQL Server Profile Providers  for  user state management.
    
*  Master Pages  for  consistent look and feel versus ASP.NET Web User Controls.
    
*  ASP.NET  2.0  Wizard control.
    
*  Database level cache invalidation  using  SqlCacheDependency instead of timeout based.
    
*  Enabling Asynchronous Order Processing built on message queuing.

What 
is  System.Transactions ?

System.Transactions 
is  a  new  transaction control  namespace   in  the .NET  2.0  Framework. It  is  a  new  way to handle distributed transactions without the overhead of COM +  registration and the COM +  catalog. Note that the Microsoft Distributed Transaction Coordinator  is  used to initiate the transactions.

See it 
in  action

The Order.Insert() method 
in  synchronous order processing uses System.Transactions to insert an order and update the inventory stock. We have implemented the Order.Insert() method by adding a reference to the System.Transaction  namespace  and wrapping the order insertion and inventory stock reduction methods inside of a TransactionScope,  as  shown  in  Code Listing  1 .

Listing 
1 . System.Transactions  in  action

using  System;
using  System.Transactions;
using  PetShop.IBLLStrategy;

namespace  PetShop.BLL {
    
///   <summary>
    
///  This is a synchronous implementation of IOrderStrategy 
    
///  By implementing IOrderStrategy interface, the developer can 
    
///  add a new order insert strategy without re-compiling the whole 
    
///  BLL.
    
///   </summary>
     public   class  OrderSynchronous : IOrderStrategy {
      ...
        
///   <summary>
        
///  Inserts the order and updates the inventory stock within 
        
///  a transaction.
        
///   </summary>
        
///   <param name="order"> All information about the order </param>
         public   void  Insert(PetShop.Model.OrderInfo order) {

            
using  (TransactionScope ts  =   new                       
TransactionScope(TransactionScopeOption.Required)) {

                dal.Insert(order);

                
//  Update the inventory to reflect the current inventory 
                
//  after the order submission.
                Inventory inventory  =   new  Inventory();
                inventory.TakeStock(order.LineItems);

                
//  Calling Complete commits the transaction.
                
//  Excluding this call by the end of TransactionScope's
                
//  scope will rollback the transaction.
                ts.Complete();
            }
        }
    }
}

In .NET Pet Shop 
3 , distributed transactions are handled by Enterprise Services and require COM +  registration. The OrderInsert  class   is  derived from a Serviced Component and transactions are handled by COM + . The service component  is  then registered  using  the regsvr32 command.

Listing 
2 . Pet Shop  3  Order Insert

using  System;
using  System.Collections;
using  System.EnterpriseServices;
using  System.Runtime.InteropServices;
...
namespace  PetShop.BLL {
   
///   <summary>
   
///  A business component to manage the creation of orders
   
///  Creation of an order requires a distributed transaction
   
///  so the Order class derives from ServicedComponents
   
///   </summary>
   [Transaction(System.EnterpriseServices.TransactionOption.Required)]
   [ClassInterface(ClassInterfaceType.AutoDispatch)]
   [ObjectPooling(MinPoolSize
= 4 , MaxPoolSize = 4 )]
   [Guid(
" 14E3573D-78C8-4220-9649-BA490DB7B78D " )]
   
public   class  OrderInsert : ServicedComponent {
      ...
      
///   <summary>
      
///  A method to insert a new order into the system
      
///  The orderId will be generated within the method and should not
      
///  be supplied as part of the order creation the inventory will be 
      
///  reduced by the quantity ordered.
      
///   </summary>
      
///   <param name="order"> All the information about the order </param>
      
///   <returns>
      
///  The new orderId is returned in the order object
      
///   </returns>
      [AutoComplete]
      
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);
         ...

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

Benefit of System.Transactions

Moving from Enterprise Services to System.Transactions simplifies deployment 
as  it does not require the use of the COM +  Catalog. In  using  the COM +  Catalog we were carrying around a lot of extra weight  for  just distributed transaction support. System.Transaction makes it really simple to program and deploy distributed applications  in  ASP.NET  2.0  applications. System.Transactions  is  also up to  50  percent more performant at runtime, due to removing the overhead of COM +  catalog lookups  for   object  instantiation. As a final benefit, System.Transactions  is  able to detect, when running against SQL Server  2005 , when a distributed transaction  is  running against two different databases that are hosted on a single instance of SQL Server  2005 . In  this   case , it  is  able to promote the distributed transaction to a local transaction, which avoids all overhead associated with distributed transaction logging / two phase commits, and significantly increases performance.

Generics

What are Generics
?

Whenever a collection of Pet Shop model objects 
is  returned, we use a collection list of the generic type  for  that  object . This  is  a  new  feature of C#  2.0  known  as  Generics.

See it 
in  Action

We can see Generics 
in  action from the GetProductsByCategory method shown  in  Code Listing  3 .

Listing 
3 . Product.cs (Pet Shop  4.0 )

      
///   <summary>
      
///  A method to retrieve products by category name
      
///   </summary>
      
///   <param name="category"> The category name to search by </param>    
      
///   <returns> A Generic List of ProductInfo </returns>
       public  IList < ProductInfo >  GetProductsByCategory( string  category) {

         
//  Return new if the string is empty
          if  ( string .IsNullOrEmpty(category))
            
return   new  List < ProductInfo > ();

         
//  Run a search against the data store
          return  dal.GetProductsByCategory(category);
}

Here 
is  the equivalent code  in  Pet Shop  3  that returns an IList:

Listing 
4 . Product.cs (Pet Shop  3 )

      
///   <summary>
      
///  A method to retrieve products by category name
      
///   </summary>
      
///   <param name="category"> The category name to search by </param>
      
///   <returns>
      
///  An interface to an arraylist of the search results
      
///   </returns>
       public  IList GetProductsByCategory( string  category) {

         
//  Return null if the string is empty
          if  (category.Trim()  ==   string .Empty) 
            
return   null ;

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

         
//  Run a search against the data store
          return  dal.GetProductsByCategory(category);
      }

Benefits of Generics

Generics allow us to 
return  strongly typed collections of objects  as  opposed to the IList collections  in  .NET Pet Shop  3 . Generic strongly typed collections offer type safety, and perform better than regular collections. Additionally, Generic strongly typed collections will show up on Visual Studio  2005  Intellisense, which can increase developer productivity.

ASP.NET 
2.0  Membership

Membership provides a common user authentication and management framework. .NET Pet Shop 
4  uses the SQL Server Membership Provider when user information  is  stored  in  SQL Server and a custom Membership Provider when user information  is  stored  in  Oracle.

See it 
in  Action

To implement Membership 
in  .NET Pet Shop  4 , the following steps are necessary:

   
1 . Configure forms authentication.

            
< authentication mode = " Forms " >
               
< forms name = " PetShopAuth "  loginUrl = " SignIn.aspx "  
                      protection
= " None "  timeout = " 60 " />
            
</ authentication >

   
2 . To use the SQL Membership Provider, we had to install the membership database. The Membership database  is  created by ASP.NET when running the following command.

      
% WinDir % Microsoft.NETFramework < .NET version > aspnet_regsql 
      
- < serverinstance >   - - A all  - d MSPetShop4Services

   
3 . Configure the SQL Membership Provider.

            
< membership defaultProvider = " SQLMembershipProvider " >
               
< providers >
                  
< add name = " SQLMembershipProvider "  
                       type
= " System.Web.Security.SqlMembershipProvider "  
                       connectionStringName
= " SQLMembershipConnString "  
                       applicationName
= " .NET Pet Shop 4.0 "  
                       enablePasswordRetrieval
= " false "  
                       enablePasswordReset
= " true "  
                       requiresQuestionAndAnswer
= " false "  
                       requiresUniqueEmail
= " false "  
                       passwordFormat
= " Hashed " />
               
</ providers >
            
</ membership >

   
4 . The ASP.NET Login control encapsulates all of the login logic. The CreateUserWizard control handles  new  user registration.

Benefit of ASP.NET 
2.0  Membership

With Membership services, we are able to use pre
- built user authentication and registration controls instead of writing them from scratch. The end result  is  less code to write  for  login, login status, user identity, user registration, and password recovery.

Also, since Membership now resides on its own database, we are able to remove the Accounts table that 
is  used  in  .NET Pet Shop  3  and use the Membership services database created by ASP.NET  2.0 .

Custom Membership Provider 
for  Oracle 10g

The .NET 
2.0  Framework includes a SQL Server Membership provider. In order to maintain user accounts when the application uses an Oracle membership database, we created a custom membership provider implementation  for  Oracle. We only implemented the methods that are used by .NET Pet Shop  4 , which are the CreateUser method and the Login method. This code, however, can be used and / or extended by any customer that wants to use Oracle10G with the ASP.NET membership services.

See it 
in  Action

The CreateUser method 
is  one of the implemented methods of the MembershipProvider  class . It provides insight into how the OracleMembershipProvider works.

Listing 
5 . OracleMembershipProvider.cs CreateUser(...)

using  System;
using  System.Configuration.Provider;

namespace  PetShop.Membership {
class  OracleMembershipProvider : MembershipProvider {
      
        
    
string  password,  string  email,  string  passwordQuestion, 
    
string  passwordAnswer,  bool  isApproved,  object  userId, 
    
out  MembershipCreateStatus status) {
   
   
//  create connection
   OracleConnection connection  =  
       
new  OracleConnection(OracleHelper.ConnectionStringMembership);
   connection.Open();
   OracleTransaction transaction 
=    
       connection.BeginTransaction(IsolationLevel.ReadCommitted);

   
try  {
      DateTime dt 
=  DateTime.Now;
      
bool  isUserNew  =   true ;

      
//  Step 1: Check if the user exists in the Users 
      
//  table: Create if not      
       int  uid  =  GetUserID(transaction, applicationId, username,  true
                          
false , dt,  out  isUserNew);
      
if  (uid  ==   0 ) {  //  User not created successfully!
         status  =  MembershipCreateStatus.ProviderError;
         
return   null ;
      }

      
//  Step 2: Check if the user exists in the Membership table: Error 
      
//  if yes
       if  (IsUserInMembership(transaction, uid)) {
         status 
=  MembershipCreateStatus.DuplicateUserName;
         
return   null ;
      }

      
//  Step 3: Check if Email is duplicate
       if  (IsEmailInMembership(transaction, email, applicationId)) {
         status 
=  MembershipCreateStatus.DuplicateEmail;
         
return   null ;
      }

      
//  Step 4: Create user in Membership table               
       int  pFormat  =  ( int )passwordFormat;
      
if  ( ! InsertUser(transaction, uid, email, pass, pFormat, salt,  "" ,
                      
"" , isApproved, dt)) {
         status 
=  MembershipCreateStatus.ProviderError;
         
return   null ;
      }

      
//  Step 5: Update activity date if user is not new
       if ( ! isUserNew) {
         
if ( ! UpdateLastActivityDate(transaction, uid, dt)) {
            status 
=  MembershipCreateStatus.ProviderError;
            
return   null ;
         }
      }

      status 
=  MembershipCreateStatus.Success;

      
return   new  MembershipUser( this .Name, username, uid, email, 
                                passwordQuestion, 
null , isApproved, 
                                
false , dt, dt, dt, dt, DateTime.MinValue);
   }
   
catch (Exception) {
      
if (status  ==  MembershipCreateStatus.Success)
         status 
=  MembershipCreateStatus.ProviderError;
      
throw ;
   }
   
finally  {
      
if (status  ==  MembershipCreateStatus.Success)
         transaction.Commit();
      
else
         transaction.Rollback();
      
      connection.Close();
      connection.Dispose();
   }
}

The unimplemented methods are left 
as  empty stubs like so:

public   override   string  GetUserNameByEmail( string  email) {
   
throw   new  Exception( " The method or operation is not implemented. " );
}

Benefit of Membership Provider 
for  Oracle 10g

We have implemented a custom Membership Provider since we want .NET Pet Shop 
4  to store membership data  in  an Oracle database  as  well  as   in  SQL Server. The provider model gives us the ability to integrate Oracle databases with ASP.NET  2.0  Membership Services simply and quickly.

ASP.NET 
2.0  Profile

In ASP.NET 
2.0 , user information can be stored across multiple visits to a Web application  in  a  new  service called Profile. The Profile implementation  for .NET Pet Shop  4  stores and retrieves users '  shopping carts, wish lists, and account information. One key point here is that many customers will find that this can replace their use of the session object almost completely, providing a transacted, cluster-safe store for user session information. By default, the Profile service serializes the data as a BLOB that it stores into the database. However, higher performance can be achieved by implementing your own profile service serialization service. For PetShop 4, a custom implementation of the Profile service was created to reduce the serialization overhead.

See it 
in  Action

   
1 . Configure the Profile Providers.

      Listing 
6 . Profile Provider Configuration

      
< 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 >

   
2 . Migrate the Anonymous Profile.

      Listing 
7 . Migrating Anonymous Profiles

      
//  Carry over profile property values from an anonymous to an 
      
//  authenticated state 
       void  Profile_MigrateAnonymous(Object sender, ProfileMigrateEventArgs e) {
          ProfileCommon anonProfile 
=  Profile.GetProfile(e.AnonymousID);

          
//  Merge anonymous shopping cart items to the authenticated 
          
//  shopping cart items
           foreach  (CartItemInfo cartItem  in  
              anonProfile.ShoppingCart.CartItems)
              Profile.ShoppingCart.Add(cartItem);

          
//  Merge anonymous wishlist items to the authenticated wishlist 
          
//  items
           foreach  (CartItemInfo cartItem  in  anonProfile.WishList.CartItems)
              Profile.WishList.Add(cartItem);

          
//  Clean up anonymous profile
          ProfileManager.DeleteProfile(e.AnonymousID);
          AnonymousIdentificationModule.ClearAnonymousIdentifier();
          
          
//  Save profile
          Profile.Save();
      }

      Listing 
8 . Shopping Cart

      
using  System;
      
using  System.Collections.Generic;
      
using  PetShop.Model;

      
namespace  PetShop.BLL {

          
///   <summary>
          
///  An object to represent a customer's shopping cart.
          
///  This class is also used to keep track of customer's wish list.
          
///   </summary>
          [Serializable]
          
public   class  Cart {

              
//  Internal storage for a cart     
               private  Dictionary < string , CartItemInfo >  cartItems  =  
                  
new  Dictionary < string , CartItemInfo > ();

              
///   <summary>
              
///  Calculate the total for all the cartItems in the Cart
              
///   </summary>
               public   decimal  Total {
                  
get  {
                      
decimal  total  =   0 ;
                      
foreach  (CartItemInfo item  in  cartItems.Values)
                          total 
+=  item.Price  *  item.Quantity;
                      
return  total;
                  }
              }

              
///   <summary>
              
///  Update the quantity for item that exists in the cart
              
///   </summary>
              
///   <param name="itemId"> Item Id </param>
              
///   <param name="qty"> Quantity </param>
               public   void  SetQuantity( string  itemId,  int  qty) {
                  cartItems[itemId].Quantity 
=  qty;
              }

              
///   <summary>
              
///  Return the number of unique items in cart
              
///   </summary>
               public   int  Count {
                  
get  {  return  cartItems.Count; }
              }

              
///   <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;
                  
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
++ ;
              }

              
///   <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="item"> Item to add </param>
               public   void  Add(CartItemInfo item) {
                  CartItemInfo cartItem;
                  
if  ( ! cartItems.TryGetValue(item.ItemId,  out  cartItem))
                      cartItems.Add(item.ItemId, item);
                  
else
                      cartItem.Quantity 
+=  item.Quantity;
              }

              
///   <summary>
              
///  Remove item from the cart based on itemId
              
///   </summary>
              
///   <param name="itemId"> ItemId of item to remove </param>
               public   void  Remove( string  itemId) {
                  cartItems.Remove(itemId);
              }

              
///   <summary>
              
///  Returns all items in the cart. Useful for looping through
              
///  the cart.
              
///   </summary>
              
///   <returns> Collection of CartItemInfo </returns>
               public  ICollection < CartItemInfo >  CartItems {
                  
get  {  return  cartItems.Values; }
              }

              
///   <summary>
              
///  Method to convert all cart items to order line items
              
///   </summary>
              
///   <returns> A new array of order line items </returns>
               public  LineItemInfo[] GetOrderLineItems() {

                  LineItemInfo[] orderLineItems 
=  
                      
new  LineItemInfo[cartItems.Count];
                  
int  lineNum  =   0 ;

                  
foreach  (CartItemInfo item  in  cartItems.Values)
                      orderLineItems[lineNum] 
=   new  LineItemInfo(item.ItemId, 
                          item.Name, 
++ lineNum, item.Quantity, item.Price);

                  
return  orderLineItems;
              }

              
///   <summary>
              
///  Clear the cart
              
///   </summary>
               public   void  Clear() {
                  cartItems.Clear();
              }
          }
      }

Benefit of the ASP.NET 
2.0  Profile

With ASP.NET 
2.0 , users '  shopping carts are stored in a database and are persisted, so that if users come back 2-3 days later, they still have their cart. Additionally, the Profile service is "on demand," whereas session state objects get re-loaded per page on any page that references it; an advantage of Profile service is that it does not get loaded unless it ' s actually needed.

Furthermore, 
using  the Profile feature we are able to remove Account and Profile tables from the existing Pet Shop  3  database—and  this  resulted  in  fewer lines of code  in  the Business Logic and Data Access Layers,  as  well.

Master Page

ASP.NET 
2.0  provides a  new  technique  for  maintaining a consistent look and feel  for  an entire Web site by  using  a Master Page. The .NET Pet Shop  4  Master Page contains the header, LoginView control, navigation menu, and HTML  for  rendering content. All of the other Pet Shop Web forms are wired to use the Pet Shop  4  Master Page.

See it 
in  Action

The .NET Pet Shop 
4  Master Page  is  depicted  in  figure  3 .


Figure 
3 . .NET Pet Shop  4  Master Page

Listing 
9 . Master Page wire - up

<% @ Page AutoEventWireup = " true "  Language = " C# "  
         MasterPageFile
= " ~/MasterPage.master "  Title = " Products "  
         Inherits
= " PetShop.Web.Products "  CodeFile = " ~/Products.aspx.cs "   %>

Benefit of ASP.NET 
2.0  Master Pages

Using the Master Page, we are able to simply create a single layout that we can reuse 
for  all of the .NET Pet Shop pages. Any changes to the layout during development are made directly to the Master Page, leaving the other pages simply  for  content. In contrast, the user  interface   in  .NET Pet Shop  3   is  implemented by encapsulating the header and navigation bar within an ASP.NET User Control called NavBar.ascx. Each of the Web forms  in  .NET Pet Shop  3  contain the NavBar user control  as  well  as  HTML to control the layout. Changing the layout would involve fumbling with the NavBar user control or modifying the HTML on each of the Web forms.

ASP.NET 
2.0  Wizard Control

The check
- out  process  in  .NET Pet Shop  4   is  contained within a single Wizard control that resides on the CheckOut page. The Wizard  is  a  new  control that provides a simple way to implement a step - by - step process. The Wizard control manages the navigation between forms, the data persistence, and the state management  in  each step.

See it 
in  Action

Figure 
4 . Check - out  Wizard control

Benefit of the ASP.NET 
2.0  Wizard Control (Click on the image  for  a larger picture)

The process of checking 
out   in  .NET Pet Shop  3  involves a series of ASP.NET pages that communicate with each other. From the shopping cart, the user can go to the check - out  page; from there, users enter their billing information and  finally  the order  is  processed. The flow  is  controlled by a custom  class  called CartController, which manages the communication between the steps  using  Session State.

Figure 
5 . .NET Pet Shop  3  Checkout Process

The Wizard Control makes implementing the checkout very simple 
in  .NET Pet Shop  4  with less code.

Database Level Cache Invalidation

New to ASP.NET 
2.0   is  the SQL Cache Dependency  object  that can be used to invalidate the cache when the data from SQL Server has been changed. Pet Shop  4  uses the SQL Cache Dependency  object   for  invalidating the Category, Products, and Item caches.

Out of the box, Pet Shop includes only the implementation 
for  table - based cache dependency. Developers can implement their own caching invalidation mechanisms by extending the CacheDependency  object . Once implemented, the CacheDependency  for  Pet Shop  4  can be configured from web.config.

Note that the SQL CacheDependency 
for  Pet Shop  4   is  only designed to run against SQL Server. For Oracle, .NET Pet Shop  4  will fall back to time - based cache expiration.

See it 
in  Action

The cache dependency 
for  SQL Server  is  shown  in  figure  6 :


Figure 
6 . Pet Shop Table Cache Dependency

Benefit of Database Level Cache Invalidation

With cache invalidation we can keep presented content consistent with the data 
in  the Pet Shop databases, yet still realize the benefits of  object  caching on the middle tier to reduce runtime processing requirements on the middle tier, and reduce database calls. This increases application scalability (it can handle more concurrent users),  while  also reducing load on the database.

Asynchronous Order Processing

One of the other changes that we have made 
is  adding an option to configure whether the ordering process should commit the transactions directly (synchronously) to the databases or to a designated queue  in  which the orders will be processed at a later point (Asynchronous). In asynchronous order processing, when a user place an order, it goes to a queue. .NET Pet Shop  4  has an implementation  for  storing  in  Microsoft Messaging Queue (MSMQ). This queue of orders can be processed later by the Order Processor console app. An advantage of  this  approach  is  that the orders database does not even have to be running  for  customers to still be able to place orders. Since MSMQ  is   using  a durable queue, all orders are still captured with no interruption  for  users, and will be inserted into the database once the processing application and the orders database come online again.

See it 
in  Action

To handle the algorithm variations between synchronous and asynchronous order processing, we use the Strategy Pattern. In the Strategy Pattern, the order placement method 
is  decoupled from the BLL.Order.Insert method. Based on the web.config setting  for  OrderStrategy, the corresponding Insert method  is  used instead. By  default , the .NET Pet Shop  is  configured to work synchronously.

To configure the Order Strategy, change the OrderStrategyClass value from OrderSynchronous to OrderAsynchronous. In addition, 
for  asynchronous order processing, MSMQ must be enabled with a  private  queue created  for  Pet Shop,  as  shown here.

      
< add key = " OrderStrategyClass "  value = " PetShop.BLL.OrderSynchronous " />
      
< add key = " OrderQueuePath "  value = " private queue path " />

Synchronous Order Placement

Figure 
7  depicts synchronous order placement. When users check  out  their orders, the checkout button click  event  handler calls the Order Insert method within the BLL. For synchronous order placement, the BLL Order  object  uses the OrderSynchronous Insert method to insert a  new  order into the Orders database and then update the Inventory database to reflect the current inventory after the order has completed submission.

Figure 
7 . Synchronous order placement

Asynchronous Order Placement

Figure 
8  depicts asynchronous order placement. On the Web site, when the user clicks the CheckOut button, the BLL Order Insert method  is  called. However, since the OrderStrategy  is  configured  for  Asynchronous, the OrderAsynchronous strategy  is  used. The OrderAsynchronous insert method plainly sends the order info to a queue.


Figure 
8 . Asynchronous Order Placement

Order Processor

The Order Processor 
is  a console application that we created to receive the orders that are  in  the Messaging implementation and transcribe these orders into the Order and Inventory databases. The Order Processor works multi - threaded, and processes orders  in  batches. It re - uses the Synchronous order strategy to insert the  new  order into the Orders database and to decrement the Inventory database.

Benefit of Asynchronous Order Processing

Processing orders asynchronously can be found 
in  many other enterprise applications. De - coupling the order process  is  one way we made .NET Pet Shop  4  perform better  as  the orders are processed  in  multi - threaded fashion.
Architecture

As with the previous versions of the .NET Pet Shop, the architecture focuses on a clean separation between user 
interface , application logic, and data. This clean separation allows us to change an implementation  in  one layer without affecting the other layers. For example, we can change the database vendor without having to change the business logic code.

The diagram 
in  figure  9  depicts the high - level logical architecture  for  .NET Pet Shop  4 . The Presentation Layer (WEB) contains the various user  interface  elements. The Business Logic Layer (BLL) holds the application logic and business components. The Data Access Layer (DAL)  is  responsible  for  interacting with the databases  for  data storage and retrieval. Each of the tiers will be discussed  in  more detail  in  the following sections.


Figure 
9 . Architecture diagram of .NET Pet Shop  4  (Click on the image  for  a larger picture)
Abstract Factory Pattern

.NET Pet Shop 
4  uses the Abstract Factory Design Pattern,  in  which interfaces are used to create families of related or dependent objects without specifying their concrete classes. One example of  this  pattern  is  within the Data Access Layer tier, which has projects  for  IDAL, DAL Factory, Oracle DAL, and SQL Server DAL. Abstract factories are created  for  caching, inventory and orders data access, messaging, and profile data access.
Presentation Layer

ASP.NET 
2.0  includes many built - in  features that increase developer productivity. In building .NET Pet Shop  4 , we have redesigned the user  interface  to take advantage of the  new  features that ASP.NET  2.0  provides, such  as  Master Pages, Themes, Skins, Wizard control, and Login control. To maintain the user accounts, we utilize the Membership provider instead of  using  ASP.NET Session state to store users '  shopping carts and favorite products. The new Profile provider can store a strongly typed shopping cart that makes programming and managing user state much easier. Using all of these features, we are able to quickly implement the Pet Shop presentation layer changes.
User Interface Enhancements

.NET Pet Shop 
4  sports a clean  new  look and feel. The  new  user  interface  supports a larger pets catalog and makes it easier  for  users to find and purchase the various  new  pets. When we changed the look and feel of the .NET Pet Shop user  interface , we had fun with the sample pets available from the .NET Pet Shop. The .NET Pet Shop now houses penguins, bugs, pandas, and even skeletons, dinosaurs, and transparent cats !  We also improve the shopping experience by adding a wish list, breadcrumb trail, and other subtleties.
Encrypting Configuration Information

The .NET Framework 
2.0  introduces a  protected  configuration feature that we used to encrypt connection strings. With  this  feature we can encrypt the sensitive database username and password information.

The .NET Pet Shop Installer will automatically run a script to encrypt the connection strings stored within the web.config file when you select the 
" full source and database install "  option.

To perform the configuration encryption on the 
" source only "  install, run the EncryptWebConfig.bat file found  in  the installed directory.

C:WINDOWSMicrosoft.NETFrameworkv2.
0.50727 aspnet_regiis.exe 
    
- pef  " connectionStrings "  
    
" C:Program FilesMicrosoft.NET Pet Shop 4.0Web "

Business Logic Layer

The business logic 
for .NET Pet Shop  4  retains much of the business logic from the .NET Pet Shop  3 , such  as  Model objects and how they are used. The few changes are the use of Generics, asynchronous order placement, and the System.Transactions  namespace .
Model Objects

.NET Pet Shop 
4  carries over Model objects from .NET Pet Shop  3 . These objects are custom lightweight classes that mimic the structure of database tables. These objects are shared across the application layers  as  a way to communicate with each other. For example, when returning multiple products  in  a category, we are returning a collection of Product Model objects.
Data Access Layer

The BLL communicates with the Data Access Layer to access data from the Pet Shop 
4  databases. .NET Pet Shop  4  uses four databases: Inventory, Orders, Membership, and Profile. As with .NET Pet Shop  3 this  version supports both Oracle and SQL Server databases.
Order and Inventory Schema

The database schema 
for  Orders and Inventory used  in  the .NET Pet Shop  4   is  ported from the .NET Pet Shop  3 . A few fields that are not used are removed. The databases have the following overall structure of tables: 


Figure 
10 . Pet Shop Orders Database


Figure 
11 . Pet Shop Inventory Database
Profile Database Schema

The Profile database 
is  used to store user specific information, such  as  account info and shopping cart contents. The database has the following overall structure of tables:


Figure 
12 . Pet Shop Profile Database
Conclusions

The Microsoft .NET Pet Shop 
4.0  application serves to highlight the key technologies and architecture that can be used to build scalable enterprise Web applications. Due to the enhancements  in  ASP.NET  2.0 , we are able to build an n - tier enterprise application more quickly, allowing us to spend time building a richer and more fully featured Pet Shop.

The key changes and 
new  features of .NET  2.0  we targeted were:

    
*  System.Transactions: Allows faster processing of transactions and easier deployment without use of the COM +  catalog.
    
*  Generics: Allows us to  return  strongly typed collections of objects  as  opposed to the IList collections  in  .NET Pet Shop  3 . Enables easier coding since Intellisense will recognize the typed objects  in  the collection.
    
*  ASP.NET  2.0  Membership Service: Provides a common user authentication and management framework that can dramatically reduce code associated with creating and maintaining user account information. Allowed us to use pre - built user authentication and registration controls instead of writing them from scratch. The end result  is  less code to write  for  login, login status, user identity, user registration, and password recovery.
    
*  ASP.NET  2.0  Profile Service: Replaces use of the session  object   for  user - specific information such  as  the shopping cart. Provides a transacted, cluster - safe store  for  user session information that can be maintained across multiple user visits to the site.
    
*  ASP.NET  2.0  Master Page: Provides a  new  technique  for  maintaining a consistent look and feel  for  an entire Web site, and makes it easy to apply  global  changes to the look and feel of a site across many pages simply by updating the Master Page.
    
*  ASP.NET  2.0  Wizard Control: A  new  server - side control that provides a simple way to implement a step - by - step process. We used it to reduce the amount of coding  in  the checkout process  for  Pet Shop  4.0 .
    
*  ASP.NET  2.0   is  the SQL Cache Dependency: Allows middle - tier  object  caches to be invalidated automatically  as  backend database information changes. This will work with SQL  2000  at the table level ( as  we did with PetShop  4 ), and with SQL Server  2005  it can also work at the individual row level. With  this  feature, cached database information can always be kept up to date  while  still taking advantage of caching to reduce load on middle - tier and database servers.
    
*  Asynchronous Order Processing Option via Messaging: While not a .NET  2.0  feature (also available  in  .NET  1.1 ), we extended PetShop  4  with the ability to optionally use messaging via MSMQ and System.Messaging versus  using  standard synchronous transactions directly to the database. This decouples the order process  for  the orders database, providing an added degree of reliability and potentially scalability.
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值