Tải bản đầy đủ (.pdf) (34 trang)

Pro .NET 2.0 Extreme Programming 2006 phần 9 pot

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (713.3 KB, 34 trang )

OdbcCommand dataCommand = new OdbcCommand();
dataCommand.Connection = dataConnection;
// Build command string
StringBuilder commandText =
new StringBuilder("DELETE FROM [Orders Details] WHERE OrdersID = ");
commandText.Append(orderID);
commandText.Append(" and ProductID = ");
commandText.Append(productID);
dataCommand.CommandText = commandText.ToString();
int rows = dataCommand.ExecuteNonQuery();
// Make sure that the DELETE worked
Assert.AreEqual(1, rows, "Unexpected Orders Details row count, gasp!");
dataConnection.Close();
lineItem = null;
}
catch(Exception e)
{
Assert.Fail("Orders Details database error: " + e.Message);
}
}
}
}
Listing 15-10. OrderDetailData.cs File
#region Using derectives
using System;
using System.Collections.Generic;
using System.Data
using System.Data.Odbc;
using System.Text;
using BusinessLayer;
#endregion


namespace DataLayer
{
public class OrderDetailData
{
public OrderDetailData()
{
}
CHAPTER 15 ■ SECOND ITERATION 253
4800ch15.qrk 5/23/06 8:20 PM Page 253
public static int InsertLineItem(int orderID, LineItem lineItem)
{
int rows = -1;
try
{
Product product = lineItem.Item;
OdbcConnection dataConnection = new OdbcConnection();
dataConnection.ConnectionString = DataUtilities.ConnectionString;
dataConnection.Open();
OdbcCommand dataCommand = new OdbcCommand();
dataCommand.Connection = dataConnection;
// Build command string
StringBuilder commandText =
new StringBuilder("INSERT INTO [Orders Details] (");
commandText.Append("OrdersID, ");
commandText.Append("ProductID, ");
commandText.Append("UnitPrice, ");
commandText.Append("Quantity, ");
commandText.Append("Discount) VALUES (");
commandText.Append(orderID);
commandText.Append(", ");

commandText.Append(product.ProductID);
commandText.Append(", ");
commandText.Append(product.Price);
commandText.Append(", ");
commandText.Append(lineItem.Quantity);
commandText.Append(", ");
commandText.Append(0);
commandText.Append(")");
dataCommand.CommandText = commandText.ToString();
rows = dataCommand.ExecuteNonQuery();
dataConnection.Close();
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
CHAPTER 15 ■ SECOND ITERATION254
4800ch15.qrk 5/23/06 8:20 PM Page 254
return rows
}
}
}
Listing 15-11. OrderDetail.cs File
#region Using directives
using System;
using System.Collections.Generic;
using System.Text;
#end region
namespace BusinessLayer
{

public class OrderDetail
{
private int orderID;
private int productID;
private decimal unitPrice;
private int quantityOrdered;
private float discount;
public OrderDetail()
{
}
public OrderDetail(int orderID,
int productID,
decimal unitPrice,
int quantityOrdered,
float discount)
{
this.orderID = orderID;
this.productID = productID;
this.unitPrice = unitPrice;
this.quantityOrdered = quantityOrdered;
this.discount = discount;
}
CHAPTER 15 ■ SECOND ITERATION 255
4800ch15.qrk 5/23/06 8:20 PM Page 255
public int OrderID
{
get
{
return this.orderID;
}

set
{
this.orderID = value;
}
}
public int ProductID
{
get
{
return this.productID;
}
set
{
this.productID = value;
}
}
public decimal UnitPrice
{
get
{
return this.unitPrice;
}
set
{
this.unitPrice = value;
}
}
public int QuantityOrdered
{
get

{
return this.quantityOrdered;
}
set
{
this.quantityOrdered = value;
}
}
CHAPTER 15 ■ SECOND ITERATION256
4800ch15.qrk 5/23/06 8:20 PM Page 256
public float Discount
{
get
{
return this.discount;
}
set
{
this.discount = value;
}
}
}
}
This concludes the test, data, and business classes. You should be able to successfully
build and test the entire solution. Once you have resolved any build or test issues, you are
ready to move on to the web layer.
Add Button to Check Out the Shopping Cart Contents Task
The first web layer task is to add a button to the DisplayShoppingCart.aspx so that users
can start the process of placing their order. Listing 15-12 shows the updated
DisplayShoppingCart.aspx code.

Listing 15-12. DisplayShoppingCart.aspx File
<%@ Import Namespace="BusinessLayer" %>
<%@ Import Namespace="DataLayer" %>
<%@ Import Namespace="System.Collections" %>
<%@ Register TagPrefix="Categories" TagName="LeftNav" Src="Categories.ascx" %>
<%@ Register TagPrefix="TopNav" TagName="TopNav" Src="TopNav.ascx" %>
<%@ Page language="c#" CodeFile="DisplayShoppingCart.aspx.cs"
AutoEventWireup="false" Inherits="NorthwindWeb.DisplayShoppingCart" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
" /><html xmlns=" /><head>
<title>Display Shopping Cart</title>
</head>
<body>
<table id="Table1" cellspacing="1" cellpadding="1" width="100%" border="0">
<tr>
<td colspan="2" style="height: 43px">
<TopNav:TopNav id="topnav" runat="server" />
</td>
</tr>
CHAPTER 15 ■ SECOND ITERATION 257
4800ch15.qrk 5/23/06 8:20 PM Page 257
<tr>
<td width="20%" valign="top" align="left">
<Categories:LeftNav id="leftnav" runat="server" />
</td>
<td valign="top" align="left">
<table width="100%">
<tr>
<th align="left">
Product</th>

<th align="left">
Price</th>
<th align="left">
Available</th>
<th align="left">
Quantity</th>
<th>
</th>
</tr>
<%
while (cartEnumerator.MoveNext())
{
%>
<form action="DisplayShoppingCart.aspx"
id="UpdateContents" method="post">
<tr>
<%
lineItem = (LineItem)cartEnumerator.Value;
Product product = lineItem.Item;
Response.Write("<td>" + product.ProductName + "</td><td>"
+ product.Price.ToString("C") + "</td><td>"
+ product.Quantity.ToString() + "</td>");
Response.Write("<input type=\"hidden\" name=\"availableQty\""+
"value=\"" + product.Quantity + "\">");
Response.Write("<input type=\"hidden\" name=\"productID\""+
"value=\"" + product.ProductID + "\">");
%>
<td>
<input type="text" size="2" name="quantity"
value="<%=lineItem.Quantity%>">

<%
Response.Write(lineItem.Message);
lineItem.Message = "";
%>
</td>
CHAPTER 15 ■ SECOND ITERATION258
4800ch15.qrk 5/23/06 8:20 PM Page 258
<td>
<input type="submit" value="Update Quantity">
</td>
</tr>
</form>
<%
}
%>
</table>
</td>
</tr>
<tr>
<td colspan="5" align="center">
<form action="CheckoutConfirmation.aspx"
id="CheckoutConfirmation" method="post">
<input type="submit" value="Checkout" />
</form>
</td>
</tr>
</table>
</body>
</html>
This will take the user to the CheckOutConfirmation.aspx page.

Create Checkout Confirmation Page Task
Now that the DisplayShoppingCart.aspx page allows the user to check out, you need to build
the confirmation page called CheckoutConfirmation.aspx, as shown in Listing 15-13.
Listing 15-13. CheckoutConfirmation.aspx File
<%@ Import Namespace="BusinessLayer" %>
<%@ Import Namespace="DataLayer" %>
<%@ Import Namespace="System.Collections" %>
<%@ Register TagPrefix="Categories" TagName="LeftNav" Src="Categories.ascx" %>
<%@ Register TagPrefix="TopNav" TagName="TopNav" Src="TopNav.ascx" %>
<%@ Page Language="c#"
CodeFile="CheckoutConfirmation.aspx.cs"
AutoEventWireup="true"
Inherits="CheckoutConfirmation" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
" /><html xmlns=" /><head>
<title>Checkout Confirmation</title>
</head>
CHAPTER 15 ■ SECOND ITERATION 259
4800ch15.qrk 5/23/06 8:20 PM Page 259
<body>
<table id="Table1" cellspacing="1" cellpadding="1" width="100%" border="0">
<tr>
<td colspan="2" style="height: 43px">
<TopNav:TopNav id="topnav" runat="server" />
</td>
</tr>
<tr>
<td width="20%" valign="top" align="left">
<Categories:LeftNav id="leftnav" runat="server" />
</td>

<td valign="top" align="left">
</td>
</tr>
</table>
</body>
</html>
This creates a basic page with the top and left navigation pieces. Now when users click the
Checkout button on the DisplayShoppingCart.aspx page, they go here. But you aren’t display-
ing anything yet. Let’s move on to the next task.
Display Shopping Cart Contents in Checkout Confirmation Page Task
You will need to retrieve the shopping cart that you have been building and display its con-
tents. To do that, enhance CheckoutConfirmation.aspx as shown in Listing 15-14.
Listing 15-14. Modified CheckoutConfirmation.aspx to Show Shopping Cart Contents
<%@ Import Namespace="BusinessLayer" %>
<%@ Import Namespace="DataLayer" %>
<%@ Import Namespace="System.Collections" %>
<%@ Register TagPrefix="Categories" TagName="LeftNav" Src="Categories.ascx" %>
<%@ Register TagPrefix="TopNav" TagName="TopNav" Src="TopNav.ascx" %>
<%@ Page language="c#"
CodeFile="CheckoutConfirmation.aspx.cs"
AutoEventWireup="false"
Inherits="CheckoutConfirmation" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
" /><html xmlns=" /><head>
<title>Checkout Confirmation</title>
</head>
CHAPTER 15 ■ SECOND ITERATION260
4800ch15.qrk 5/23/06 8:20 PM Page 260
<body>
<table id="Table1" cellspacing="1" cellpadding="1" width="100%" border="0">

<tr>
<td colspan="2" style="height: 43px">
<TopNav:TopNav id="topnav" runat="server" />
</td>
</tr>
<tr>
<td width="20%" valign="top" align="left">
<Categories:LeftNav id="leftnav" runat="server" />
</td>
<td valign="top" align="left">
<table width="100%">
<tr>
<th align="left">
Product</th>
<th align="left">
Price</th>
<th align="center">
Quantity</th>
<th>
</th>
</tr>
<%
while ( cartEnumerator.MoveNext() )
{
LineItem lineItem = (LineItem)cartEnumerator.Value;
Product product = lineItem.Item;
Response.Write("<tr><td>" + product.ProductName + "</td><td>"
+ product.Price.ToString("C") + "</td><td align=\"center\">"
+ lineItem.Quantity.ToString() + "</td></tr>");
}

%>
</table>
</td>
</tr>
</table>
</body>
</html>
Next, you need to add code to the CheckoutConfirmation.aspx.cs class to get the shop-
ping cart from the session, as shown in Listing 15-15.
CHAPTER 15 ■ SECOND ITERATION 261
4800ch15.qrk 5/23/06 8:20 PM Page 261
Listing 15-15. Modified CheckoutConfirmation.aspx.cs to Get the Shopping Cart
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using BusinessLayer;
using DataLayer;
public partial class CheckoutConfirmation : System.Web.UI.Page
{
protected ShoppingCart cart = null;
protected IDictionaryEnumerator cartEnumerator = null;
protected void Page_Load(object sender, System.EventArgs e)

{
if ( Session["cart"] != null )
{
cart = (ShoppingCart)Session["cart"];
}
else
{
cart = new ShoppingCart();
cart.Quantity = 0;
Session["cart"] = cart;
}
cartEnumerator = cart.GetCartContents();
}
}
Now when the users go to this page, if they have any items in their shopping cart, those
items will be displayed. But this isn’t much different from the DisplayShoppingCart.aspx page.
So, next you will subtotal the dollar amounts of all the items displayed.
Subtotal Shopping Cart Line Items and Display Results Task
To get the subtotal, you need to first enhance CheckoutConfirmation.aspx to display the subto-
tal, as shown in Listing 15-16.
CHAPTER 15 ■ SECOND ITERATION262
4800ch15.qrk 5/23/06 8:20 PM Page 262
Listing 15-16. Modified CheckoutConfirmation.aspx to Show the Subtotal
<%@ Import Namespace="BusinessLayer" %>
<%@ Import Namespace="DataLayer" %>
<%@ Import Namespace="System.Collections" %>
<%@ Register TagPrefix="Categories" TagName="LeftNav" Src="Categories.ascx" %>
<%@ Register TagPrefix="TopNav" TagName="TopNav" Src="TopNav.ascx" %>
<%@ Page language="c#"
CodeFile="CheckoutConfirmation.aspx.cs"

AutoEventWireup="false"
Inherits="CheckoutConfirmation" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
" /><html xmlns=" /><head>
<title>Checkout Confirmation</title>
</head>
<body>
<table id="Table1" cellspacing="1" cellpadding="1" width="100%" border="0">
<tr>
<td colspan="2" style="height: 43px">
<TopNav:TopNav id="topnav" runat="server" />
</td>
</tr>
<tr>
<td width="20%" valign="top" align="left">
<Categories:LeftNav id="leftnav" runat="server" />
</td>
<td valign="top" align="left">
<table width="100%">
<tr>
<th align="left">
Product</th>
<th align="left">
Price</th>
<th align="center">
Quantity</th>
<th>
</th>
</tr>
<%

while ( cartEnumerator.MoveNext() )
{
LineItem lineItem = (LineItem)cartEnumerator.Value;
Product product = lineItem.Item;
Response.Write("<tr><td>" + product.ProductName + "</td><td>"
CHAPTER 15 ■ SECOND ITERATION 263
4800ch15.qrk 5/23/06 8:20 PM Page 263
+ product.Price.ToString("C") + "</td><td align=\"center\">"
+ lineItem.Quantity.ToString() + "</td></tr>");
total += product.Price * lineItem.Quantity;
}
%>
</table>
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td valign="top" align="right">
Order Total:
<%=total.ToString("C")%>
</td>
</tr>
</table>
</body>
</html>
Then you need to add one line of code to the CheckoutConfirmation.aspx.cs class, as
storage for the subtotal. Listing 15-17 shows that enhancement.
Listing 15-17. Modified CheckoutConfirmation.aspx.cs to Store the Subtotal
using System;
using System.Collections;

using System.Configuration;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using BusinessLayer;
using DataLayer;
public partial class CheckoutConfirmation : System.Web.UI.Page
{
protected ShoppingCart cart = null;
protected IDictionaryEnumerator cartEnumerator = null;
protected decimal total = 0;
CHAPTER 15 ■ SECOND ITERATION264
4800ch15.qrk 5/23/06 8:20 PM Page 264
protected void Page_Load(object sender, System.EventArgs e)
{
if ( Session["cart"] != null )
{
cart = (ShoppingCart)Session["cart"];
}
else
{
cart = new ShoppingCart();
cart.Quantity = 0;
Session["cart"] = cart;
}

cartEnumerator = cart.GetCartContents();
}
}
}
Now you can display the subtotal. Next, you need to add a button to cancel checking out
in case users change their mind.
Add Button to Cancel the Checkout Task
For the next task, you will add a button to CheckoutConfirmation.aspx that will allow users to
exit the checkout process and return to their shopping experience. Listing 15-18 shows the
enhanced CheckoutConfirmation.aspx page.
Listing 15-18. CheckoutConfirmation.aspx with a Cancel Button
<%@ Import Namespace="BusinessLayer" %>
<%@ Import Namespace="DataLayer" %>
<%@ Import Namespace="System.Collections" %>
<%@ Register TagPrefix="Categories" TagName="LeftNav" Src="Categories.ascx" %>
<%@ Register TagPrefix="TopNav" TagName="TopNav" Src="TopNav.ascx" %>
<%@ Page language="c#"
CodeFile="CheckoutConfirmation.aspx.cs"
AutoEventWireup="false"
Inherits="CheckoutConfirmation" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
" /><html xmlns=" /><head>
<title>Checkout Confirmation</title>
</head>
CHAPTER 15 ■ SECOND ITERATION 265
4800ch15.qrk 5/23/06 8:20 PM Page 265
<body>
<table id="Table1" cellspacing="1" cellpadding="1" width="100%" border="0">
<tr>
<td colspan="2" style="HEIGHT: 43px">

<TopNav:TopNav id="topnav" runat="server" />
</td>
</tr>
<tr>
<td width="20%" valign="top" align="left">
<Categories:LeftNav id="leftnav" runat="server" />
</td>
<td vAlign="top" align="left">
<table width="100%">
<tr>
<th align="left">
Product</th>
<th align="left">
Price</th>
<th align="center">
Quantity</th>
<th>
</th>
</tr>
<%
while ( cartEnumerator.MoveNext() )
{
LineItem lineItem = (LineItem)cartEnumerator.Value;
Product product = lineItem.Item;
Response.Write("<tr><td>" + product.ProductName + "</td><td>"
+ product.Price.ToString("C") + "</td><td align=\"center\">"
+ lineItem.Quantity.ToString() + "</td></tr>");
total += product.Price * lineItem.Quantity;
}
%>

</table>
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td valign="top" align="right">
Order Total:
<%=total.ToString("C")%>
</td>
</tr>
CHAPTER 15 ■ SECOND ITERATION266
4800ch15.qrk 5/23/06 8:20 PM Page 266
<tr>
<td colspan="2" align="right">
<form id="login" method="post" runat="server">
<asp:Button ID="CancelButton" runat="server" Text="Cancel"></asp:Button>
</form>
</td>
</tr>
</table>
</body>
</html>
Then you need to enhance the CheckoutConfirmation.aspx.cs file to handle the redirection
back to the shopping experience, as shown in Listing 15-19.
Listing 15-19. CheckoutConfirmation.aspx.cs with Redirection Added
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Drawing;

using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using BusinessLayer;
using DataLayer;
public partial class CheckoutConfirmation : System.Web.UI.Page
{
protected ShoppingCart cart = null;
protected IDictionaryEnumerator cartEnumerator = null;
protected decimal total = 0;
protected void Page_Load(object sender, System.EventArgs e)
{
if ( Session["cart"] != null )
{
cart = (ShoppingCart)Session["cart"];
}
else
{
cart = new ShoppingCart();
cart.Quantity = 0;
CHAPTER 15 ■ SECOND ITERATION 267
4800ch15.qrk 5/23/06 8:20 PM Page 267
Session["cart"] = cart;
}
cartEnumerator = cart.GetCartContents();
}
protected void CancelButton_Click(Object sender, System.EventArgs e)

{
Response.Redirect("DisplayShoppingCart.aspx", true);
}
}
}
Add Button to Process Order Request Task
The last task is to add a button to CheckoutConfirmation.aspx that will act as acceptance of the
user’s intention to check out and process the order. First, you will add the button to the page,
as shown in Listing 15-20.
Listing 15-20. CheckoutConfirmation.aspx with a Complete Order Button
<%@ Import Namespace="BusinessLayer" %>
<%@ Import Namespace="DataLayer" %>
<%@ Import Namespace="System.Collections" %>
<%@ Register TagPrefix="Categories" TagName="LeftNav" Src="Categories.ascx" %>
<%@ Register TagPrefix="TopNav" TagName="TopNav" Src="TopNav.ascx" %>
<%@ Page language="c#"
CodeFile="CheckoutConfirmation.aspx.cs"
AutoEventWireup="false"
Inherits="CheckoutConfirmation" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
" /><html xmlns=" /><head>
<title>Checkout Confirmation</title>
</head>
<body>
<table id="Table1" cellspacing="1" cellpadding="1" width="100%" border="0">
<tr>
<td colspan="2" style="height: 43px">
<TopNav:TopNav id="topnav" runat="server" />
</td>
</tr>

<tr>
<td width="20%" valign="top" align="left">
<Categories:LeftNav id="leftnav" runat="server" />
</td>
CHAPTER 15 ■ SECOND ITERATION268
4800ch15.qrk 5/23/06 8:20 PM Page 268
<td valign="top" align="left">
<table width="100%">
<tr>
<th align="left">
Product</th>
<th align="left">
Price</th>
<th align="center">
Quantity</th>
<th>
</th>
</tr>
<%
while ( cartEnumerator.MoveNext() )
{
LineItem lineItem = (LineItem)cartEnumerator.Value;
Product product = lineItem.Item;
Response.Write("<tr><td>" + product.ProductName + "</td><td>"
+ product.Price.ToString("C") + "</td><td align=\"center\">"
+ lineItem.Quantity.ToString() + "</td></tr>");
total += product.Price * lineItem.Quantity;
}
%>
</table>

</td>
</tr>
<tr>
<td>&nbsp;</td>
<td valign="top" align="right">
Order Total:
<%=total.ToString("C")%>
</td>
</tr>
<tr>
<td colspan="2" align="right">
<form id="login" method="post" runat="server">
<asp:Button id="CompleteOrderButton" runat="server"
Text="Complete Order">
</asp:Button>
<asp:Button id="CancelButton" runat="server" Text="Cancel"></asp:Button>
</form>
</td>
</tr>
</table>
</body>
</html>
CHAPTER 15 ■ SECOND ITERATION 269
4800ch15.qrk 5/23/06 8:20 PM Page 269
Then you will enhance the CheckoutConfirmation.aspx.cs class with a method to handle
the Complete Order button click, as shown in Listing 15-21.
Listing 15-21. CheckoutConfirmation.aspx.cs with an Order Completion Method
using System;
using System.Collections;
using System.Configuration;

using System.Data;
using System.Drawing;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using BusinessLayer;
using DataLayer;
public partial class CheckoutConfirmation : System.Web.UI.Page
{
protected ShoppingCart cart = null;
protected IDictionaryEnumerator cartEnumerator = null;
protected decimal total = 0;
private void Page_Load(object sender, System.EventArgs e)
{
if ( Session["cart"] != null )
{
cart = (ShoppingCart)Session["cart"];
}
else
{
cart = new ShoppingCart();
cart.Quantity = 0;
Session["cart"] = cart;
}
cartEnumerator = cart.GetCartContents();
}
protected void CompleteOrderButton_Click(Object sender, System.EventArgs e)

{
// Get the current customer
User user = (User)Session["User"];
Customer customer = CustomerData.FindCustomerByUserName(user.UserName);
CHAPTER 15 ■ SECOND ITERATION270
4800ch15.qrk 5/23/06 8:20 PM Page 270
// Create the new Order
int orderID = OrderData.InsertOrder(customer);
// Do order completion stuff and redirect to OrderConfirmation page
cart = (ShoppingCart)Session["cart"];
cartEnumerator = cart.GetCartContents();
while (cartEnumerator.MoveNext())
{
LineItem lineItem = (LineItem)cartEnumerator.Value;
OrderDetailData.InsertLineItem(orderID, lineItem);
lineItem.Item.Quantity -= lineItem.Quantity;
ProductData.UpdateQuantity(lineItem.Item);
}
// Empty the cart
Session["cart"] = null;
Response.Redirect("OrderConfirmation.aspx?orderID=" + orderID, true);
}
protected void CancelButton_Click(object sender, System.EventArgs e)
{
Response.Redirect("DisplayShoppingCart.aspx", true);
}
}
}
The User class in the BusinessLayer will need to be updated. This class should look like
Listing 15-22.

Listing 15-22. Updated User.cs File
#region Using directives
using System;
using System.Collections.Generic;
using System.Text;
#endregion
namespace BusinessLayer
{
public class User
{
private string userName;
private string password;
CHAPTER 15 ■ SECOND ITERATION 271
4800ch15.qrk 5/23/06 8:20 PM Page 271
public User()
{
}
public User(string userName, string password)
{
this.userName = userName;
this.password = password;
}
public string UserName
{
get
{
return this.userName;
}
set
{

this.userName = value;
}
}
public string Password
{
get
{
return this.password;
}
set
{
this.password = value;
}
}
}
}
In addition, the ProductData class in the DataLayer will need to updated, as shown in
Listing 15-23.
Listing 15-23. Updated ProductData.cs File
#region Using directives
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.Odbc;
CHAPTER 15 ■ SECOND ITERATION272
4800ch15.qrk 5/23/06 8:20 PM Page 272
using System.Text;
using BusinessLayer;
#endregion

namespace DataLayer
{
public class ProductData
{
public ProductData()
{
}
public static ArrayList GetProductsByCategory(int categoryID)
{
ArrayList products = new ArrayList();
try
{
OdbcConnection dataConnection = new OdbcConnection();
dataConnection.ConnectionString = DataUtilities.ConnectionString;
dataConnection.Open();
OdbcCommand dataCommand = new OdbcCommand();
dataCommand.Connection = dataConnection;
// Build command string
StringBuilder commandText =
new StringBuilder("SELECT * FROM Products WHERE CategoryID=");
commandText.Append(categoryID);
commandText.Append(" AND UnitsInStock > 0");
dataCommand.CommandText = commandText.ToString();
OdbcDataReader dataReader = dataCommand.ExecuteReader();
while (dataReader.Read())
{
Product product = new Product();
product.ProductID = dataReader.GetInt32(0);
product.ProductName = dataReader.GetString(1);
product.CategoryID = dataReader.GetInt32(3);

product.Price = dataReader.GetDecimal(5);
product.Quantity = dataReader.GetInt16(6);
products.Add(product);
}
CHAPTER 15 ■ SECOND ITERATION 273
4800ch15.qrk 5/23/06 8:20 PM Page 273
dataConnection.Close();
}
catch(Exception e)
{
Console.WriteLine("Error: " + e.Message);
}
return products;
}
public static Product GetProduct(int ProductID)
{
Product product = null;
try
{
OdbcConnection dataConnection = new OdbcConnection();
dataConnection.ConnectionString = DataUtilities.ConnectionString;
dataConnection.Open();
OdbcCommand dataCommand = new OdbcCommand();
dataCommand.Connection = dataConnection;
// Build command string
StringBuilder commandText =
new StringBuilder("SELECT * FROM Products WHERE ProductID = ");
commandText.Append(productID);
dataCommand.CommandText = commandText.ToString();
OdbcDataReader dataReader = dataCommand.ExecuteReader();

if (dataReader.Read())
{
product = new Product();
product.ProductID = dataReader.GetInt32(0);
product.ProductName = dataReader.GetString(1);
product.CategoryID = dataReader.GetInt32(3);
product.Price = dataReader.GetDecimal(5);
product.Quantity = dataReader.GetInt16(6);
}
dataCommand.Close();
}
catch (Exception e)
{
Console.WriteLine("Error: " + e.Message);
}
CHAPTER 15 ■ SECOND ITERATION274
4800ch15.qrk 5/23/06 8:20 PM Page 274
return product;
}
public static ArrayList SearchForProducts(string searchString)
{
ArrayList products = new ArrayList();
try
{
OdbcConnection dataConnection = new OdbcConnection();
dataConnection.ConnectionString = DataUtilities.ConnectionString;
dataConnection.Open();
OdbcCommand dataCommand = new OdbcCommand();
dataCommand.Connection = dataConnection;
// Build command string

StringBuilder commandText =
new StringBuilder("SELECT * FROM Products WHERE ProductName LIKE '%');
commandText.Append(searchString);
commandText.Append("%'");
dataCommand.CommandText = commandText.ToString();
OdbcDataReader dataReader = dataCommand.ExecuteReader();
while (dataReader.Read())
{
product = new Product();
product.ProductID = dataReader.GetInt32(0);
product.ProductName = dataReader.GetString(1);
product.CategoryID = dataReader.GetInt32(3);
product.Price = dataReader.GetDecimal(5);
product.Quantity = dataReader.GetInt16(6);
products.Add(product);
}
dataConnection.Close();
}
catch (Exception e)
{
Console.WriteLine("Error: " + e.Message);
}
return products;
}
CHAPTER 15 ■ SECOND ITERATION 275
4800ch15.qrk 5/23/06 8:20 PM Page 275
public static void UpdateQuantity(Product product)
{
try
{

OdbcConnection dataConnection = new OdbcConnection();
dataConnection.ConnectionString = DataUtilities.ConnectionString;
dataConnection.Open();
OdbcCommand dataCommand = new OdbcCommand();
dataCommand.Connection = dataConnection;
// Build command string
StringBuilder commandText =
new StringBuilder("UPDATE Products Set UnitsInStock = ");
commandText.Append(product.Quantity);
commandText.Append(" WHERE ProductID = ‘");
commandText.Append(product.ProductID);
commandText.Append("'");
dataCommand.CommandText = commandText.ToString();
int rows = dataCommand.ExecuteNonQuery();
dataConnection.Close();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}
This should complete all the tasks for the Display Checkout Confirmation user story. But
remember that a story is not complete until the customer says so. That is where the accept-
ance test for this user story comes into play.
Acceptance Testing
The acceptance test for a story is defined by the customer and automated (if possible) by the
acceptance tester. Our customer defined the acceptance test for the Display Checkout Confir-
mation user story as follows:

• Click a Checkout button on the Display Shopping Cart page to check out.
• Display a Checkout Confirmation page that lists all the items currently in the shopping
cart.
• See a subtotal dollar amount of the order.
CHAPTER 15 ■ SECOND ITERATION276
4800ch15.qrk 5/23/06 8:20 PM Page 276
• When a Continue button is clicked on the Checkout Confirmation page, the order
should be built and handed off for processing.
• A Cancel button should also be displayed on the Checkout Confirmation page. When
clicked, that button should take the user back to the Display Shopping Cart page.
Developing the Remaining User Stories
This iteration has four other user stories. As in the first iteration, they are being developed in
parallel with the Display Checkout Confirmation user story, which we focused on here. Each
user story has its own acceptance tests written by the customer and automated by the accept-
ance testers, where possible.
The developers switch pairs at least once a day. They take a test-driven approach with the
remaining user stories, just as with the user story described in this chapter.
All the developers keep track of the time they spend on the tasks that they picked for this
iteration. When the tracker comes around (at least twice a week), the developers report the
actual time they have spent on their tasks.
You can download the source code for this iteration from the Source Code area of the
Apress website (www.apress.com).
Coach’s Journal
During this iteration, I noticed that the team is getting better at pairing. The pairs were better
at switching who was on the keyboard and who was not. I also saw that the team members are
getting more acclimated to their environment, and that overall communication is starting to
occur more frequently and openly.
Also, the acceptance testers are getting more comfortable with testing early rather than
later. The acceptance testers were extremely helpful in working with the customer to define
the acceptance tests and determining what could be automated.

As in the last iteration, I spoke with our customer frequently to make sure that he was
comfortable with what was going on during the iteration. When our customer had concerns or
questions, I addressed them immediately. This iteration, our customer became keenly aware
of a need for securing the website during the ordering process. I sat down with him, and we
discussed several options that he had in this area. I can see that he is already thinking about
security stories for the next release.
Summary
By now, you should be getting a feel for the XP process. You have seen how to take a test-
driven development approach to coding. You have witnessed how an iterative approach can
provide a lot of feedback in the form of unit and acceptance tests. Daily stand-ups and the
graphs and charts generated by the tracker create a better environment for communication.
All of this communication allows the team members to better gauge where they are at meeting
their targets.
You are well on your way down the XP path.
CHAPTER 15 ■ SECOND ITERATION 277
4800ch15.qrk 5/23/06 8:20 PM Page 277

×