
Beginning ASP.NET 2
.0.pdf
Performance
CREATE PROCEDURE dbo.usp_NewsByID @NewsID int
AS
SELECT * FROM News WHERE NewsID = @NewsID
The first thing to notice is that there is an extra line between the CREATE PROCEDURE and the AS statement. This is where parameters are placed and is very similar to parameters for methods, as discussed in Chapter 9. In this case you have a parameter called @NewsID, whose data type is an int (an integer). Parameters in SQL are always preceded by the at symbol (@). The value for this parameter is supplied in the same way as if this SelectCommand were a SQL statement, by the SelectParameters:
<SelectParameters>
<asp:ControlParameter Name=”NewsID” Type=”Int32”
Control=”GridView1” PropertyName=”SelectedValue”></asp:ControlParameter> </SelectParameters>
The value of the Name attribute is passed through into the @NewsID parameter of the stored procedure. Within the procedure the parameter is used to restrict the rows returned, so only rows where the NewsID matches the value of the @NewsID parameter are returned.
The same technique applies for the other stored procedures. For example, to update a news story the following stored procedure is used:
CREATE PROCEDURE dbo.usp_NewsUpdate @DateToShow datetime, @Description text,
@PictureUrl varchar(50), @Category varchar(50), @Title varchar(50), @NewsID int
AS
UPDATE News
SET DateToShow = @DateToShow, Description = @Description, PictureUrl = @PictureUrl, Category = @Category, Title = @Title
WHERE NewsID = @NewsID
There are more parameters here, with each being separated by a comma. The data types are also slightly different from what you might expect, because SQL handles strings differently from .NET. For example, the Description and PictureUrl are both a String type in .NET, but there are two data types used in SQL. This is because SQL allows for strings of a fixed maximum length, in which case the varchar data type is used with the maximum in parentheses, or for unlimited strings, in which case the text data type is used. A varchar only defines the maximum length for the string, and doesn’t necessarily store that much data. For example, PictureUrl is declared as varchar(50), but if the PictureUrl only contains 10 characters, then only 10 characters are stored. There is another data type for handling strings, which is char, but that does store all characters. So if PictureUrl was declared as char(50) and only 10 were used, what is stored is the actual string followed by 40 spaces. Those spaces would be
539

Chapter 14
automatically added when the string was inserted into the database and would remain when the data is fetched, which would mean you might have to truncate your data when displaying it. So unless you are storing a string that you know is fixed (three-character currency codes for example), then a varchar data type is best.
We don’t have time to go into more detail on the SQL itself, because it’s really outside the scope of what we’re teaching. The important thing to remember is that using stored procedures improves performance as well as centralizing data access statements.
For more on SQL get hold of Beginning SQL, by Paul Wilton and John Colby, and for more on database access in ASP.NET see Beginning ASP.NET 2.0 Databases by John Kauffman. Both books are also by Wrox Press.
Strongly Typed Collections
Chapter 9 briefly mentioned generics as a way to improve code in a number of areas: readability, reducing errors, and performance. We’re not going to go into much detail about generics — it’s a wide topic — but it does have an impact on performance, especially in regard to collections. So it’s worth reiterating the differences between a normal collection and a generic one. Take the standard ArrayList as an example, which can be used like this:
Imports System.Collections
Dim myList As New ArrayList() myList.Add(“abc”) myList.Add(“def”)
Dim s As String = DirectCast(myList(0), String)
Now there’s nothing wrong with this code; it works fine and is probably used in lots of web sites, but it’s not the most efficient way to store a list of strings. This is because the ArrayList stores objects, so when a string is placed into the list it is converted to an Object data type. Likewise, when fetching data from the list it needs to be converted back to its native type. This conversion between types is an overhead.
With a generic collection the overhead is avoided because the collection is automatically of the correct type. Instead of an ArrayList, a generic List can be used:
Imports System.Collections.Generic
Dim myList As List(Of String) myList.Add(“abc”) myList.Add(“def”)
Dim s As String = myList(0)
When declaring the List you set the type of object it contains — in this case String. When objects are added to the list they don’t need to be converted because the list is only storing strings. Similarly, no conversion is required when reading a value from the list. As a result, there’s a performance improvement when adding data and fetching it.
540

Performance
The added benefit of generic collections is that you can’t store anything other than the defined type in them. This reduces potential error situations, because attempted storage of the wrong data type will result in a compiler error.
Session State
Chapter 6 explained the stateless nature of web sites, and that without programming they forget information between requests. The ASP.NET controls retain information about their state, and the Profile retains information about a user, but there is no storage of data about a session, unless this is coded into the application. The session can be described as the time spent browsing a site; from the moment you first start browsing to the moment you close your browser. That’s an important point, because once the browser closes the session ends — if you start browsing after closing the browser you have a new session.
In previous versions of ASP.NET many things that are now part of the framework had to be coded, and the session was often used for temporary storage of items such as user details, shopping carts, and so on. With the Profile storing user preferences and the Membership services storing user details, the need for session storage has reduced. Membership is covered in Chapter 4 and the Profile is covered in Chapter 11, so refer to those chapters for details of what is stored.
This is important to know because session state takes resources, both the processor and memory, and if it’s not required then why have it there? By default the session is enabled for read and write access, but this can be disabled if your application is never going to use session state. You can do this in the application configuration file (Web.config), by using the sessionState element:
<sessionState mode=”Off” />
This turns off session state, which will save valuable resources.
If session state is required, but none of the pages need to update it, then it can be made read-only (or turned off for pages) by changing the enableSessionState attribute of the pages element in
Web.config:
<pages enableSessionState=”ReadOnly” />
The values can be False to turn off session state, True to enable it, and ReadOnly to make it read-only. The ReadOnly property can also be applied to the Page directive on individual pages as follows:
<%@ Page enableSessionState=”ReadOnly” %>
It should be noted that for low-user sites, turning off session state probably won’t make much difference, but it could have an impact on a high-performance site with a large number of active users. You should also make sure that developers know that session state is turned off for a site, otherwise they may try to code for it when it isn’t available.
541

Chapter 14
View State
The way ASP.NET server controls retain their state is a great boon for the programmer and makes construction of web sites far easier than it used to be. The retention of state is called the view state and is stored within a hidden control on the page, so no extra programming is needed. This does, however, come with a downside, and that downside is that the size of the page increases, which in turn leads to a slowerloading page. Now by and large the advantages of view state outweigh the disadvantages, but it’s useful to understand how it works. Take a look at the Wrox United home page, which contains many controls. There are some links at the top, a menu and the login and shopping cart on the left, and then the main content, which consists of the news. All of these are handled by controls, and only the central content is specific to the individual page — the rest is supplied by the Master page, and therefore appears on every page.
This is important from the performance perspective because the view state for the menu is 2780 bytes. That might not seem like a lot, but it’s sent on every page request and returned on every postback. Plus that’s just the menu control; others have view state too. Essentially any control that participates in postback will probably have view state and thus the more controls, the more view state.
Like session state, view state can also be disabled, both at the page and control level, using the EnableViewState attribute. To enable this for all controls on a page (or the Master page) you modify the Page directive as follows:
<%@ Page EnableViewState=”False” %>
For controls the attribute is the same. For example, the menu control in site.master could have view state disabled like so:
<asp:Menu id=”MyMenu” runat=”server”
EnableViewState=”False” />
The menu continues to work as before, but now has no view state so the page has more than 2000 bytes less to transmit every time. This technique can be applied to other controls, especially those such as grids or lists. One case is the Players page (players.aspx), which uses a DataList to show the player details, and has no interaction. Turning off view state for this control reduces the page size by another 2700 bytes.
In fact, many of the controls don’t actually need view state, and those that do interact with the page might be able to have view state disabled. This is different from previous versions of ASP.NET where disabling view state stopped some controls from working. View state can be disabled for the entire application in a similar way to session state, by modifying the pages element in the Web.config file:
<pages enableViewState=”false” />
If this was done, you could then turn on view state for those controls that need it.
Later in the chapter you look at how you can find out how large controls are and how much view state they store. But first, the next section looks at how you can use code to improve performance.
542

Performance
Pages and Code
Many techniques can be used in code to help with performance, from simple use of framework classes to a properly architected application. Some techniques will only come from experience, some will only be relevant to certain applications, and some are things you should generally know.
One of these general points is to know the .NET Framework class library. You don’t have to know it inside out, nor remember all of the classes, but it’s a good idea to familiarize yourself with the classes and namespaces. It’s definitely worthwhile spending some time just browsing through the reference documentation, getting a feeling for what’s possible. For example, many string handling routines are built right into the String object, there are data type conversion classes, collections for storing multiple copies of objects, and so on.
Data Binding and Postback
If you aren’t using data source controls for your data binding, one thing you should make sure you do is only to bind when you need to; data binding involves fetching the data from the database (and we’ve already said data access is expensive) and binding each row and column from the data to the rows and columns in a control. There’s no point doing all of that data access and binding unless it’s absolutely necessary. For example, consider the following code:
Protected Sub Page_Load(ByVal Sender As Object, ByVal E As EventArgs)
GridView1.DataSource = SomeCollection
GridView1.DataBind()
End Sub
This simply binds a GridView control to a collection of data (the data is irrelevant for this discussion), but remember that if the page has buttons that post back, then the Page_Load event is run every time, so the data is bound every time. If the grid isn’t going to change, and it retains its state, why rebind it? Using the IsPostback property can avoid this:
Protected Sub Page_Load(ByVal Sender As Object, ByVal E As EventArgs)
If Note Page.IsPostback Then
GridView1.DataSource = SomeCollection
GridView1.DataBind()
End If
End Sub
Now the grid is only bound the first time the page is loaded. This may seem like an obvious point, but you’d be surprised how easy it is to forget, and remember that database access is relatively expensive — it consumes both time and resources.
If you are updating data and you do need to rebind the grid after a postback, you can bind in the button event as well, after the data has been changed. What you don’t want to do is bind the grid in when the page loads, update the data, and then rebind the grid again.
543

Chapter 14
Object References
One standard performance optimization is the use of references to objects or items in collections. For example, imagine you are processing some user input, perhaps a name, entered into the text box with an ID of NameTextBox. You have to work with the entered name in several places in some code, so you do this:
ValidateName(NameTextBox.Text)
UpdateDatabase(NameTextBox.Text)
DisplayName(NameTextBox.Text)
You can see that the Text property of the name is referenced three times. What happens if you realize the user could have entered leading and trailing spaces, but that your routines need these stripped off? You might be tempted to just trim the spaces each time, like so:
ValidateName(NameTextBox.Text.Trim())
UpdateDatabase(NameTextBox.Text.Trim())
DisplayName(NameTextBox.Text.Trim())
A much better solution is to only reference the name once, storing the value in a local variable as follows:
Dim Name As String = NameTextBox.Text.Trim()
ValidateName(Name)
UpdateDatabase(Name)
DisplayName(Name)
Not only is this easier to read, but it’s also more efficient — you’re performing the Trim once, and only referencing the Text property once.
In general this technique should be used wherever you reference objects properties, or call methods, several times. There’s probably very little performance improvement for referencing a Text property a few times, but it does sometimes aid readability, and it would certainly improve performance in a high-volume site; consider the search box on Amazon — how many times a day does that get used?
StringBuilder Versus String Concatenation
Joining strings together is a common occurrence and the technique most people use is concatenation, like so:
Dim FirstName As String = “Bill”
Dim LastName As String = “Barker”
Dim FullName As String = FirstName & “ “ & LastName
Here the ampersand (&) is used to join strings; the FullName is created from the FirstName, a space, and then the LastName. With just these three strings, trying to improve performance probably isn’t worthwhile, but when more strings are added together there is a significant overhead. The reason is that strings are immutable, which means once they are created they can never be changed. For example, consider creating a mail message to send to new members of the Wrox United fan club:
544

Performance
Dim Message As String = “Dear “ & MemberName & _
“. Welcome to the Wrox United Fan Club. As a member you’ll“ & _
“receive regular newsletters to keep you up-to-date with the“ & _
“activities of the club, special discounts in the club store,“ & _
“advance notice of special events, and many more exciting freebies.”
EmailMessage.Body = Message
This is fine as it stands because most of this is literal strings — that is, text within quotation marks. When literal strings are concatenated, the compiler will join them together so there is no overhead. But what if the strings are user-supplied? For example, you might have the following code, where the strings are taken from a Web Form:
Dim Address As String = AddressLine1.Text & AddressLine2 & AddressLine3 & _
AddressLine4.Text & AddressLine5.Text
This also is fine, and despite what you might read elsewhere this is still very efficient because the compiler knows how many strings there are and can optimize the code for you. We’ve mentioned these two scenarios because certain books and articles explicitly state that this code is very inefficient; ignore them, because the code is fine. Where you might have a performance problem is when the number of strings is large (more than 10, say) or unknown. For example, imagine a string being built up from some rows in a database (returned as a SqlDataReader, rdr), where you might have the following code:
Dim str As String While rdr.Read()
str &= rdr.GetString(0)
End While
Return str
This loops while the reader has some data and appends the data from column 0 to the string variable str. As it stands, this is very inefficient because the compiler doesn’t know how many strings there will be, so it cannot perform any optimization. Although this looks innocuous you have to remember that strings are immutable, so once created they are never changed. The preceding code, therefore, doesn’t really append one string to another; it actually creates a new string made up from the two being appended. This new string is then assigned to str. To improve performance the StringBuilder class can be used, which is in the System.Text namespace:
Imports System.Text
Dim sb As New StringBuilder() While rdr.Read()
sb.Append(rdr.GetString(0)) End While
Return sb.ToString()
When the StringBuilder object is created it allocates a buffer of memory, and as strings are appended to it using the Append class, the buffer gradually fills up. When full, another chunk of memory is allocated ready for more strings, so memory allocation happens infrequently and in chunks. This is far more efficient than appending strings to each other where the memory allocation happens for each string.
545

Chapter 14
In general you should use a StringBuilder object where you are appending a large number of strings, or where the number of strings is unknown.
Picking the Right Collection
Numerous collections exist and at first glance some seem just as good as others, but you need to carefully consider the use of the collection to ensure optimal performance. The following section lists the collections and what their best use is:
The ArrayList dynamically resizes as items are added and is best used to store custom object types when the data changes frequently — for example, when you are performing frequent inserts and deletes.
The HashTable is a collection of pairs of data (a key and a value) and is best suited for data that doesn’t change frequently, and is especially useful for data that is queried often.
The ListDictionary should only be used to store small amounts of data, generally fewer than 10 items.
The NamevalueCollection is a sorted collection of keys and values and is best for data that is already sorted. It is efficient for data that frequently changes or needs to be queried often.
The Queue provides first-in, first-out storage, so it should be used when sequentially ordered data is required.
The SortedList stores keys and values in a sorted order. This makes adding data slow, because existing items need to be rearranged to ensure the new item is in the correct order. A SortedList is best for data that doesn’t change often.
The Stack provides first-in, first-out storage, so it should be used when sequentially ordered data is required.
The StringCollection is a strongly typed ArrayList, so it is useful for storing strings in an arbitrary order, and for strings that change frequently.
The StringDictionary is a strongly typed HashTable, so it is useful when strings need to be stored and those strings don’t change often.
Using the correct collection not only improves performance but also reduces the potential for errors. For example, if you need to store strings, use a StringCollection or a StringDictionary, because they only store strings; trying to store another data type results in a compiler error, so your code is protected against storage of incorrect types.
Now it’s time to turn away from the coding aspects of performance and look toward a feature that ASP.NET provides for us. Although coding and configuration is still required, it’s not the code itself that will improve performance; rather, it is ASP.NET itself.
546

Performance
Caching
Caching is the term given to keeping a copy of something for later use, and provides a way of avoiding resource-intensive operations. For example, we’ve already talked about databases being expensive in terms of performance, but there’s no real way to avoid them with data-driven sites. So what you can do is minimize database access, by fetching data and storing it elsewhere (but not another slow resource such as a file). Another type of caching can just involve operations that don’t need to be done multiple times; storing the result of that operation saves having to redo it when required.
Caching can be used in many ways, but luckily ASP.NET has made some of these easier for us.
Page Caching
You already know that ASP.NET pages are compiled, but probably haven’t thought much about it. After all, it works, so what’s the big deal? Well, knowing how it works means you can understand how page caching works, and means you can use it effectively. The following set of diagrams show how the compilation works and how it affects caching. Figure 14-4 starts with the first request of a page, which is compiled and stored on disk in intermediate language; this isn’t a fully compiled executable, but is a shortened source code format. The .NET runtime then compiles the intermediate code, which executes, and the result of that execution is the HTML that is returned to the browser.
Disk
ASP.NET page |
Compiler |
|
.NET CLR
Runtime
Intermediate Code
Figure 14-4
The second time the page is requested (see Figure 14-5), assuming the source hasn’t been changed, the intermediate code is still available, so the compilation stage is bypassed.
547

Chapter 14
Disk
ASP.NET page |
Compiler |
|
.NET CLR
Runtime
Intermediate Code
Figure 14-5
So already we have an improvement, just by the intermediate code being cached. What can also happen is that the page output can be cached, if designed as part of the page. When the intermediate code is compiled, the HTML is not only returned to the browser, but also stored on disk, as shown in Figure 14-6.
Disk
ASP.NET page |
Compiler |
|
Disk
.NET CLR
Runtime
Intermediate Code
Figure 14-6
548