Beginning ASP.NET 2.0 With CSharp (2006) [eng]
.pdf
Chapter 9
Brackets would be needed if multiple lines were to be run within the code:
while (rdr.Read())
{
Console.WriteLine(rdr[“Column1”].ToString());
Console.WriteLine(rdr[“Column2”].ToString());
}
The while loop tests the condition before the loop starts, which means that the code block for the loop might never get executed. To guarantee at least one execution, you can use the do loop with the condition at the end:
do
// loop contents while (condition);
Or if you need multiple lines run, you use the following:
do
{
// loop contents
}
while (condition);
Because the condition is not tested until after the loop contents have been executed the first time, this type of loop is not suitable for use with a DataReader object. For example, you cannot do this:
do
{
if (rdr[“ItemCost”] < 0)
break;
}
while (rdr.Read());
Although this is perfectly valid, the code will fail unless Read has already been called. The reason for this is that when a DataReader object is first opened, it doesn’t point at the first record, but rather before it. So the act of calling Read moves to the first record. In the preceding code, if no Read has been called, the access to rdr[“ItemCost”] will fail because there will not be a valid record.
The for Loop
The for loop is useful when the number of iterations is known, and is most often used when counting through numeric values or performing some action a set number of times. For example:
for (int counter=0; counter < 10; counter++)
‘ loop code
The syntax for this is as follows:
for (starting condition; test condition; counter change)
318
Code
The starting condition sets the starting point of the loop. It can include the variable declaration, but it doesn’t have to (although this does affect the scope of the variable — a topic you’ll be seeing later in the “Variable Scope and Lifetime” section). For example, consider the following loop:
for (int counter=0; counter < 10; counter++)
This loop is equivalent to the following loop:
int counter;
for (counter=0; counter < 10; counter++)
The loop will continue while the test condition is true, so the preceding examples will loop while the counter variable is less than 10. The counter change section changes the counter value. In the examples, it adds one each time around the loop (remember that ++ is a shortcut for adding 1 to a variable). Loops can go down as well as up. Here the counter starts at 10 and decreases by 1 every time around the loop (-- subtracts 1 from a variable) — the loop will stop when counter reaches 0:
for (int counter=10; counter > 0; counter--)
Like the while and do loops, for loops can be exited during processing:
for (int counter=0; counter < 10; counter++)
{
// loop code
if (SomeFunction(counter)) break;
// loop code
}
Here, if SomeFunction returns true, the loop is executed directly. Any code below the break statement is ignored.
The foreach Loop
The foreach loop is used for looping through collections or arrays and has a variety of uses, and unlike the for loop, you don’t need to know in advance the number of times the loop will run. Regardless of what you’re looping through, the syntax is the same:
foreach (Type LoopVariable in Collection)
{
// code to run in loop
}
The parts of this are as follows:
Collection is the object containing the items to be looped through. It doesn’t have to be a collection (from System.Collections), but it can be an array.
LoopVariable is the name of the variable that will be assigned to each individual entry from
Collection.
Type is the data type of LoopVariable.
319
Chapter 9
For example, consider the following code that loops through a string array:
string[] Names = {“Dave”, “Dan”, “Chris”, “Chris”, “John”}; string AllNames;
foreach (string Name in Names) AllNames += Name + “ “;
This first creates an array of names, plus a variable to hold all of the names concatenated. When the loop starts, Name is assigned to the first entry in the array, Dave, and this is appended to the AllNames variable. The next time around the loop, Name is assigned to the next entry in the array, Dan, and so on. When all items in the array have been processed, the loop ends.
Like the for statement, there is a second form of this loop where the loop variable is declared outside of the loop:
string Name;
foreach (Name in Names) AllNames += Name + “ “;
For the purposes of the loop, this is exactly the same as declaring the variable in the loop itself, but it does affect the scope of the variable (more on that in the “Variable Scope and Lifetime” section).
Collections and lists are used a lot in .NET programming, so foreach is very useful. In Wrox United, foreach is used as part of the shop, when the checkout is reached (this is Checkout.aspx). The shop allows you to buy multiple items, and these are put into a shopping cart — this is a custom object called Cart, which contains a collection of CartItem objects. You look at the creation of the shopping cart later, but when you check out, the items in the cart need to be added to the database, and because it is a collection, foreach is ideal.
This chapter won’t go into the checkout function in complete detail, but here’s what you have:
A ShoppingCart object that contains the cart. This is stored in the Profile as Cart (the profile is covered in Chapter 5).
A collection of CartItem objects. This is stored as the Items property of the ShoppingCart.
To iterate through the items in the cart, you could use the following code:
foreach (CartItem item in Profile.Cart.Items)
{
}
Each time through the loop, item would contain the actual item from the cart. When the user proceeds to the checkout, you need to do the following:
Create an order in the database, adding it into the Orders table.
Loop through the items in the cart and add each item to the OrderLines table.
320
Code
This sounds like a lot of work, and is actually quite a lot of code (around 80 lines, including comments), but it is really simple. It builds on some of the data techniques discussed in Chapter 8, and though those aren’t covered in detail here, the code should be familiar. Here’s where the actual items from the cart are added to the database:
cmd.CommandText = “INSERT INTO OrderLines(OrderID, ProductID, Quantity, Price) “ + “VALUES (@OrderID, @ProductID, @Quantity, @Price)”;
cmd.Parameters.Clear(); cmd.Parameters.Add(“@OrderID”, SqlDbType.Int); cmd.Parameters.Add(“@ProductID”, SqlDbType.Int); cmd.Parameters.Add(“@Quantity”, SqlDbType.Int); cmd.Parameters.Add(“@Price”, SqlDbType.Money);
cmd.Parameters[“@OrderID”].Value = OrderID; foreach (CartItem item in Profile.Cart.Items)
{
cmd.Parameters[“@ProductID”].Value = item.ProductID; cmd.Parameters[“@Quantity”].Value = item.Quantity; cmd.Parameters[“@Price”].Value = item.Price;
cmd.ExecuteNonQuery();
}
The first line simply sets the SQL statement used to insert the items, and the following lines create the parameters and set the OrderID. The object cmd is a SqlCommand object, with an associated connection. Within the loop, the details of each item are copied to the parameters, and then the query is executed — this occurs for each order item, so the SQL statement happens each time. After all of the items have been added to the database, the Items collection of the shopping cart is cleared.
Give all of this looping and testing a try, by working out how well Wrox United is doing. The following Try It Out has you loop through the fixtures to see how many goals have been scored and how many games have been won, lost, or drawn.
Try It Out |
Looping and Making Decisions |
1.Create a new Web Form called Decisions.aspx and set this as the start page. Remember to place the code in a separate file when you create the Web Form.
2.Add six labels and some text, so the page looks like Figure 9-7. You can just type the text directly onto the page, and make sure you add the labels in top to bottom order, so Label1 is at the top and Label6 is at the bottom.
Figure 9-7
321
Chapter 9
3.View the code file for the page, and at the top of the file add the following two using statements (don’t worry about what these are — they’re covered later in the “Namespaces” section):
using System.Data;
using System.Data.SqlClient;
4.Create the Page_Load event procedure by double-clicking the form in design view. In the Page_Load event, add the following code (remember you can copy it from the finished samples if you don’t want to type it all):
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[ “WroxUnited”].ConnectionString);
SqlCommand cmd = new SqlCommand(“select * from Fixtures”, conn); SqlDataReader rdr;
int wins = 0; int losses = 0; int draws = 0; int goalsFor = 0;
int goalsAgainst = 0; int winRatio;
conn.Open();
rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
while (rdr.Read())
{
goalsFor += Convert.ToInt32(rdr[“GoalsFor”]); goalsAgainst += Convert.ToInt32(rdr[“GoalsAgainst”]);
if (goalsFor > goalsAgainst) wins++;
else if (goalsFor < goalsAgainst) losses++;
else draws++;
}
Label1.Text = wins.ToString();
Label2.Text = losses.ToString();
Label3.Text = draws.ToString();
Label4.Text = goalsFor.ToString();
Label5.Text = goalsAgainst.ToString();
if (losses == 0)
{
Label6.Text = “No losses - a perfect season.”; return;
}
winRatio = Convert.ToInt32((wins / losses) * 10);
switch (winRatio)
322
Code
{
case 0:
Label6.Text = “No wins. Relegation is a certainty.”; break;
case 1: case 2:
Label6.Text = “Less than 20%. Very poor.”; break;
case 3: case 4: case 5:
Label6.Text = “Under half. Could do better.”; break;
case 6: case 7:
Label6.Text = “Winning more than losing. Excellent.”; break;
default:
Label6.Text = “A high ratio - near the top of the table.”; break;
}
5.Modify the web.config file, changing the <connectionStrings/> section so that it is the same as the one for the Wrox United site. You can copy this section from web.config in the Wrox United site to save the typing — it’s the section that looks like this:
<connectionStrings>
<add name=”WroxUnited” connectionString=”Data Source=.\SQLEXPRESS; AttachDbFilename=|DataDirectory|WroxUnited.mdf;Integrated Security=True;User Instance=True”/>
</connectionStrings>
6.Save the files and run the page to see Figure 9-8.
Figure 9-8
The output isn’t spectacular, but it’s the code that’s interesting, so take a look at how it works.
323
Chapter 9
How It Works
First you have the variable declarations, starting with a connection to the database, a command to fetch the fixtures, and a reader to iterate through the fixtures:
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[ “WroxUnited”].ConnectionString);
SqlCommand cmd = new SqlCommand(“select * from Fixtures”, conn); SqlDataReader rdr;
Next you have the variables to hold the counts of the wins, losses, draws, and goals scored, and the win ratio:
int wins = 0; int losses = 0; int draws = 0; int goalsFor = 0;
int goalsAgainst = 0; int winRatio;
After the variables are declared, the database is opened and the data fetched:
conn.Open();
rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
With the data reader full of data, the loop is started; the Read method returns a Boolean value of false when it has read the last record. This will continue until there are no more records:
while (rdr.Read())
{
Within the loop, the totals are incremented, the values from the reader being converted into integers before being used to increment the totals:
goalsFor += Convert.ToInt32(rdr[“GoalsFor”]); goalsAgainst += Convert.ToInt32(rdr[“GoalsAgainst”]);
Now comes the first decision, incrementing the number of wins, losses, and draws:
if (goalsFor > goalsAgainst) wins++;
else if (goalsFor < goalsAgainst) losses++;
else draws++;
}
After the loop has finished, the totals can be displayed in the labels:
Label1.Text = wins.ToString();
Label2.Text = losses.ToString();
324
Code
Label3.Text = draws.ToString();
Label4.Text = goalsFor.ToString();
Label5.Text = goalsAgainst.ToString();
The ratio of wins to losses needs to be counted next (despite the fact that given the team’s performance you can almost assume this will be 0). However, before doing the calculation, you need to ensure that the number of losses isn’t 0, otherwise a division-by-zero exception would occur. So if the number of losses is 0, a message is displayed and Return forces the Page_Load event handler to exit:
if (losses == 0)
{
Label6.Text = “No losses - a perfect season.”; return;
}
If the team has lost at least one match, the ratio is calculated — this will be a number between 0 and 10 to represent the percentage groups:
winRatio = Convert.ToInt32((wins / losses) * 10);
Now the message can be displayed depending on the win ratio. Remember that the case statements are tried in the order in which they are declared, so the default will match a win ratio of higher than 80%:
switch (winRatio)
{
case 0:
Label6.Text = “No wins. Relegation is a certainty.”; break;
case 1: case 2:
Label6.Text = “Less than 20%. Very poor.”; break;
case 3: case 4: case 5:
Label6.Text = “Under half. Could do better.”; break;
case 6: case 7:
Label6.Text = “Winning more than losing. Excellent.”; break;
default:
Label6.Text = “A high ratio - near the top of the table.”; break;
}
All of these statements, the loops and decisions, are fairly simple on their own, but together they show you the power of what code can do. You can build up functionality with more and more statements, as the complexity of your applications requires.
The next section introduces the topic of namespaces, a fundamental way in which code can be arranged.
325
Chapter 9
Namespaces
Namespaces are a way to logically group related code. A namespace is simply a name, and that name can include periods to provide grouping. For example, the namespace for the data handling code of ADO.NET is in System.Data, whereas the SQL Server-specific code is in System.Data.SqlClient. All of the .NET data types you saw earlier are in the System namespace. Namespaces can also be created for your own code, so for the Wrox United code the namespace is Wrox.Web. This is defined in the classes within the App_Code directory.
Namespaces are important for several reasons:
Grouping related code means it’s easier to find related items. For example, if you want to find all of the data handling code, you know it’s located within the System.Data namespaces. This can be useful when using the documentation.
Namespaces provide more readable code, because if the namespace is known, only the data type is required to define a variable. For example, consider declaring a SqlConnection object without having the namespace known to the program:
System.Data.SqlClient.SqlConnection conn;
This requires more typing as well as being hard to read. However with the namespace known, this can be reduced to:
SqlConnection conn;
Namespaces allow both the compiler and IntelliSense to find the variable types.
To use a namespace, you use the using statement:
using System.Data;
using System.Data.SqlClient;
You can test to see if this is working by taking these lines out of the sample in the previous Try It Out. See what happens in the code editor — IntelliSense attempts to indicate a problem, and you’ll receive a compiler error if you try to run the page.
Within your code, you can declare namespaces by use of the Namespace statement — you place this around your classes. For example:
namespace Wrox.Web
{
// class goes here
}
You can use the same namespace in multiple files, so you can split your code into physical files (a file for each class is a good idea), and the namespace spans those classes. The namespace is logical and not physical — it isn’t restricted to a single file.
Now that you know how code works and how it can be organized, you can move onto the subject of classes, a topic that underlies the whole of .NET.
326
Code
Working with Classes
Object orientation sounds complex and scary, one of those things people tell you that you have to master before you become a proper programmer. Ignore them — there’s no such thing as a proper programmer, it’s a term that doesn’t really mean anything. Object orientation is worth learning though, and it will make you a better programmer — everyone can be a better programmer, and this is just one step along the ladder. It’s important not to worry about this topic, because object orientation is actually quite simple. It can get complex, but at the basic level, it’s easy to get into and doesn’t require a degree in rocket science (unless you work at NASA, in which case knowledge of astrophysics might be useful).
Throughout the book you’ve already seen plenty of objects. In fact, everything you’ve seen has been an object: the ASP.NET controls are objects, data access is done via objects, ASP.NET pages are objects, and even the data types are objects. This is one of the underpinnings of .NET — everything is an object. The following paragraphs define a few terms so you can see how these fit in with the existing objects and new objects you create.
First is the difference between objects, classes, and instances. A class is a template for an object; it defines what the object will be able to do — think of it as a cookie cutter, defining the shape of the cookie. An object or instance is a class that’s been created — it’s the actual cookie, after it’s freed from the cutter. Look at these terms with an example using SqlConnection:
SqlConnection conn;
This defines the variable conn as being of type SqlConnection. As it stands, however, this isn’t usable, because it just defines the type of object — the object doesn’t yet exist. You’ve only defined the shape of the cookie cutter, and haven’t actually cut the cookie. To create the cookie, create an instance of the object:
conn = new SqlConnection();
This can also be done at declaration time:
SqlConnection conn = new SqlConnection();
Use of the new keyword creates the instance, and after it is created, the object becomes usable.
The properties of a class define the characteristics of that class. For example, the SqlConnection object has a property called ConnectionString, which defines the details of the database being connected to. Another property is State, which indicates what state the connection is in (open, closed, and so on):
if (conn.State == ConnectionState.Open)
{
// The connection is open
}
The methods of a class define the actions that can be performed, so for the SqlConnection, you can Open or Close it:
conn.Open();
327
