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

249 out of 297 rated this helpful - Rate
this topic

Gregory Leake
Microsoft Corporation

Alan Le, Alex Arkhipov, Mike Hanley, and Steve Nyholm
Vertigo Software,
Inc.

February 2006

Applies to:
    Microsoft .NET Framework 2.0

    Microsoft Visual Studio 2005
    Microsoft
Windows Server 2003
    Microsoft Internet Information
Services
    Microsoft Message Queuing

    Microsoft SQL Server 2005
    Oracle 10g
Database

Summary: The .NET Pet Shop application is designed to show
the best practices for building enterprise, n-tier .NET 2.0 applications that
may need to support a variety of database platforms and deployment scenarios.
(25 printed pages)

Click here
to download .NET Pet Shop 4.0.msi.

Contents

Executive
Overview

Productivity

Migration
from ASP.NET 1.1 to 2.0

Architecture

Abstract
Factory Pattern

User
Interface Enhancements

Encrypting
Configuration Information

Model
Objects

Order
and Inventory Schema

Profile
Database Schema

Conclusions

Executive Overview

The .NET Pet Shop application is designed to show the best practices for
building enterprise, n-tier .NET 2.0 applications that may need to support a
variety of database platforms and deployment scenarios.

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.

    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.NET\Framework\<.NET version>\aspnet_regsql
    -S <server\instance> -E -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:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_regiis.exe
-pef "connectionStrings"
"C:\Program Files\Microsoft\.NET Pet Shop 4.0\Web"

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.

    【转载】:http://msdn.microsoft.com/en-us/library/aa479070.aspx

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

时间: 2024-08-07 00:12:11

Microsoft .NET Pet Shop 4: Migrating an ASP.NET 1.1 Application to 2.0的相关文章

Microsoft .NET Pet Shop 4

https://msdn.microsoft.com/en-us/library/aa479070.aspx Executive Overview The .NET Pet Shop application is designed to show the best practices for building enterprise, n-tier .NET 2.0 applications that may need to support a variety of database platfo

Help-.NET-.NET Pet Shop 4.0:ReadMe

ylbtech-Help-.NET-.NET Pet Shop 4.0:ReadMe 1.返回顶部 1. Microsoft .NET Pet Shop 4 ReadMe © 2005 Microsoft Corporation. All rights reserved.  Contents This documentation provides installation instructions for Microsoft .NET Pet Shop 4. Installation Prere

[转]ASP.NET MVC 3 Application Upgrader

本文转自:http://aspnet.codeplex.com/releases/view/59008 Recommended Download      ASP.NET MVC 3 Application Upgrader application,            431K, uploaded             Apr 11, 2011             -            37377 downloads Release Notes This standalone ap

ASP.NET中利用Application和Session统计在线人数、历史访问量

先来简单说一下ASP.NET中的Application和Session 下图是我们非常熟悉的Web应用程序的结构: 在这张图中,Web服务器中运行的Web应用程序就是我们所说的Application,每个客户端与Web服务器之间建立的连接就可以看做是一个Session.比如现在服务器端运行的是一个论坛系统,那么现在这个正运行在服务器端的论坛系统的软件就可以看做Application,而每个在线的用户与之建立的连接就相当于一个Session. 那么很容易就会理解,Application是共享的,相

微软讲座视频: 下一代vNext平台:ASP.NET vNext与MVC6,WebAPI 3.0 新特性 下载

 微软下一代平台vNext: .NET 5.0.ASP.NET vNext与MVC6,WebAPI 3.0 新特性 录像视频下载 http://t.cn/RPYhcuG  微软与开源实战训练营QQ群 203822816 微软MSDN俱乐部QQ群 29754721, 微软MSDN特邀讲师:徐雷  Frank Xu Lei倾力打造!新浪微博:http://weibo.com/frankxulei  @@老徐FrankXuLei 系统讲解NET vNext 新特性ASP.NET vNext新特性,

微软讲座视频: 微软下一代vNext平台:ASP.NET vNext与MVC6,WebAPI 3.0 新特性

微软下一代平台vNext: .NET 5.0.ASP.NET vNext与MVC6,WebAPI 3.0 新特性 录像视频下载 http://t.cn/RPYhcuG  微软与开源实战训练营QQ群 203822816 微软MSDN俱乐部QQ群 29754721, 微软MSDN特邀讲师:徐雷  Frank Xu Lei倾力打造!新浪微博:http://weibo.com/frankxulei  @@老徐FrankXuLei 讲课系统NET vNext 新特性ASP.NET vNext新特性, MV

通过TeamCity实现ASP.NET Core Web Application的自动编译及发布

下载 TeamCity,当前版本:2017.1 TeamCity插件:.NET Core Support .NET Core SDK,当前版本:1.0.1 安装 安装TeamCity.需要注意的地方: 安装路径 端口,这里是9080 安装Server和Agent Windows 服务的账号(这里建议使用系统账号而不是用户账号) 安装完成后会自动打开TeamCity的页面,开始初始化配置.数据目录,数据库和管理用用户. 安装TeamCity插件:.NET Core Support. 安装插件有两种

ASP.NET 应用程序(Application)生命周期概述

原文:ASP.NET 应用程序(Application)生命周期概述 引用MSDN:ASP.NET 应用程序生命周期概述 本 主题概述应用程序生命周期,列出重要的生命周期事件,并描述如何编写适合应用程序生命周期的代码.在 ASP.NET 中,若要对 ASP.NET 应用程序进行初始化并使它处理请求,必须执行一些处理步骤.此外,ASP.NET 只是对浏览器发出的请求进行处理的 Web 服务器结构的一部分.了解应用程序生命周期非常重要,这样才能在适当的生命周期阶段编写代码,达到预期的效果. 应用程序

ASP.NET 5 入门——Application Startup

ASP.NET 5 入门--Application Startup? Startup 类? 在 ASP.NET 5 中,Startup 类是一个应用的入口点,我们可以为不同环境配置不同的内容. 编译器会查找项目文件夹下的所有 *.cs 文件进行编译,而运行时会寻找所有命名空间下类名为 Startup 的类作为启动方式. 注解 可以通过设置 project.json 文件选择需要(或不需要)编译的文件和文件夹:也可以设置在不同的程序集中搜索 Startup 类. Startup 类必须定义一个 C