
Beginning ASP.NET 2.0 With CSharp (2006) [eng]
.pdf
Chapter 14
tables, which indexes to use, and so on, and it does this every time. After the plan has been created, the stored procedure is also compiled and the compiled copy is the one that is executed.
A much better solution would be to work out the execution plan and store it for subsequent use. So when a query is executed, the plan is ready and doesn’t need recalculating. You can do this by way of stored procedures, which are a way of wrapping up SQL into an easily manageable form. It’s a bit like a procedure or a function in that the SQL to be executed can be wrapped in a stored procedure and the stored procedure name used to execute it. Consider this SqlDataSource:
<asp:SqlDataSource id=”SqlDataSource1” runat=”server” ConnectionString=”<%$ConnectionStrings:WroxUnited%>”
SelectedCommand=”SELECT [ProductID], [Name], [Description], [Price], [PictureURL] FROM [Products]”
</asp:SqlDataSource>
Using a stored procedure, the code would look like this:
<asp:SqlDataSource id=”SqlDataSource1” runat=”server” ConnectionString=”<%$ConnectionStrings:WroxUnited%>” SelectedCommand=”usp_Products” SelectCommandType=”StoredProcedure”
</asp:SqlDataSource>
From the code perspective, this is already better for two reasons: it makes the code neater and easier to read; and it abstracts the SQL into a central place, the database. Having the SQL in the database is good because it’s the natural place for it — it’s code that deals directly with the tables and columns. It also means your code is easier to manage because you know that all of the SQL is in one place. A sensible naming scheme means stored procedures are easy to locate — we’ve used the naming scheme of usp_ followed by the name of the table. The prefix usp_ is a common one and denotes User Stored Procedure — many of system stored procedures that SQL Server provide are prefixed by sp_, so adding the u makes it obvious which procedures are ours and which are the server’s. The other addition when using stored procedures is the SelectCommandType attribute, which tells the SqlDataSource that the command being issued is a stored procedure, rather than a textual SQL statement.
The syntax for creating a stored procedure is as follows:
CREATE PROCEDURE ProcedureName
AS
SqlStatement
ProcedureName is the name of the procedure (usp_Products) and SqlStatement is the SQL statement that will be run when the stored procedure is called. So, how do you actually go about creating stored procedures, and what exactly do they contain? You give this a go in the following Try It Out.
Try It Out |
Creating and Using Stored Procedures |
1.In the WroxUnited VWD application for this chapter (Chapters/Begin/Chapter14/ WroxUnited), load the Shop.aspx file and change the SelectCommand to usp_Products.
528

Performance
2.Add the following new attribute to the SqlDataSource:
SelectCommandType=”StoredProcedure”
3.Save the file.
4.Select the Database Explorer tab and expand WroxUnited.mdf, which will appear under Data Connections (see Figure 14-1).
Figure 14-1
5.Right-click Stored Procedures and select Add New Stored Procedure from the menu.
6.Modify the procedure so that it looks like this:
CREATE PROCEDURE dbo.usp_Products
AS
SELECT ProductID, [Name], Description, Price, PictureURL
FROM Products
ORDER BY [Name]
7.Save and close the procedure.
8.Right-click WroxUnited.mdf and select Close Connection from the menu. This ensures that when you run the application, you don’t receive an error telling you that the data file couldn’t be opened because it is in use by another process.
9.On the Wrox United web site, navigate to the Shop page to confirm that the page displays as you expect it to. You’ll see no change from the user perspective, because stored procedures are a programming concept, and apart from speed, have no impact on what the user sees.
How It Works
The first thing to look at is the stored procedure itself:
CREATE PROCEDURE dbo.usp_Products
AS
529

Chapter 14
SELECT ProductID, [Name], Description, Price, PictureURL
FROM Products
ORDER BY [Name]
So you can see that the name of your stored procedure is usp_Products. The dbo part indicates the owner of the procedure, and in this case the owner is dbo — a synonym for the database owner. Anything after the AS statement is the actual procedure itself, and yours simply consists of a SELECT statement. In fact, this is the same SELECT statement that was generated by VWD when the SqlDataSource was created, with the addition of an ORDER BY clause to order the results by the product name. This is a good way to combine the ease of data source controls with stored procedures — let the designer create the control, and then copy the SQL statement into a stored procedure and use the procedure name in the data source control. The column Name is wrapped within square brackets because Name is a keyword within SQL. The stored procedure works without the brackets, but using them tells SQL Server that this use of Name is a column, and saves SQL Server having to work that out on its own.
The procedure name starts with usp_ for usability and historic reasons. You don’t want your stored procedure to have the same name as the table, but keeping the table name is useful, because it helps to identify which stored procedures are related to which tables. In SQL Server, the system-stored procedures start with sp_ (an acronym for stored procedure), so using that as a prefix would confuse your stored procedures with the system ones. So usp_ (an acronym for user stored procedure) is generally used instead. This isn’t a requirement, but you’ll find that it is a common practice.
SQL is both case and context insensitive, meaning no line continuation characters are required.
In the ASP.NET page, the SQL statement has been replaced by the name of the stored procedure. By default, the SqlDataSource control expects a SQL statement, so when using a stored procedure, you have to add the SelectCommandType attribute, setting its value to StoredProcedure. This tells ASP.NET that the command isn’t a SQL statement to be executed, but that the named stored procedure should be used instead.
Modifying Stored Procedures
To modify a stored procedure, you open it (double-click or right-click and select Open from the menu) from within the Database Explorer, where you will be presented with the following:
ALTER PROCEDURE dbo.usp_Products
AS
SELECT ProductID, Name, Description, Price, PictureURL
FROM Products
ORDER BY Name
Notice that CREATE PROCEDURE has been replaced with ALTER PROCEDURE. The CREATE statement is only used when creating stored procedures, and subsequently ALTER is used. After you’ve completed your changes, perhaps changing the columns being selected, you can just save the file, and the procedure in the database will be updated. In fact, just the matter of saving a new query changes CREATE to
ALTER.
Stored Procedure Names
In addition to using the usp_ prefix, you may want to use a consistent naming scheme for your stored procedures. For example, a procedure that returns all rows from a table could simply have the prefix
530

Performance
plus the table name, or perhaps be GetTableName, usp_Products or usp_GetProducts. For a procedure that returns on a single row, the single form would work well: usp_GetProduct, or usp_GetProductByID. Stored procedures that modify data should include the modification type: usp_InsertProduct or usp_UpdateProduct. Alternatively, place the table name before the action: usp_ProductInsert, usp_ProductUpdate, or usp_ProductGet. This latter scheme means that all procedures related to the same table will show together in the procedure list, while the former scheme means that procedures are grouped by their action. There’s no single way of doing this, and it’s really just a matter of preference, but pick one scheme and stick to it, so that you can find procedures easily.
Using the Query Builder
If you don’t want to use the copy-and-paste method for creating SQL statements, you can use the Query Builder. You can access this when creating or modifying a stored procedure by selecting the Insert SQL item from the right mouse menu — just right-click anywhere in the stored procedure editor window to get the menu. You first see a window where you select the table (see Figure 14-2).
Figure 14-2
You can double-click a table to add it, or select the table and use the Add button. When you have selected your table and closed the window, you see the Query Builder, shown in Figure 14-3.
The Query Builder is an excellent way of learning SQL, because it presents you with an easy-to-under- stand graphical way of designing queries, and shows the actual SQL statement. When the Query Builder is closed, the SQL is inserted into the stored procedure. It doesn’t update the existing SQL but inserts the new SQL instead.
If you want to use the Query Builder to edit the existing SQL in a stored procedure, you need to place the cursor within the SQL block when editing the procedure — this block is outlined in blue — and select the Design SQL Block from the right mouse menu. When you select this option, the Query Builder comes up right away with the existing SQL in place.
531

Chapter 14
Figure 14-3
Modifying Data and Parameters
The query and stored procedure shown earlier only fetch data, but three other types of queries can be run: updates, inserts, and deletes. Just like SELECT queries, these update queries can also be converted into stored procedures. As an example, take a look at the administrative Edit News page, where the second SqlDataSource is defined like so:
<asp:sqldatasource id=”SqlDataSource2” runat=”server” ConnectionString=”<%$ ConnectionStrings:WroxUnited %>” SelectCommand=”SELECT * FROM [News] WHERE [NewsID] = @NewsID”
UpdateCommand=”UPDATE [News] SET [DateToShow] = @DateToShow, [Description] = @Description, [PictureURL] = @PictureURL, [Category] = @Category, [Title] = @Title WHERE [NewsID] = @NewsID”
InsertCommand=”INSERT INTO [News] ([DateToShow], [Description], [PictureURL], [Category], [Title]) VALUES (@DateToShow, @Description, @PictureURL, @Category, @Title)”
DeleteCommand=”DELETE FROM [News] WHERE [NewsID] = @NewsID” OnDeleted=”SqlDataSource2_Deleted” OnInserted=”SqlDataSource2_Inserted” OnUpdated=”SqlDataSource2_Updated”>
The important parts are the SelectCommand, UpdateCommand, InsertCommand, and DeleteCommand attributes, which define the SQL used when fetching and modifying data. There are also a number of parameters, which provide the mapping from the ASP.NET controls to the queries:
532

Performance
<DeleteParameters>
<asp:Parameter Type=”Int32” Name=”NewsID”></asp:Parameter> </DeleteParameters>
<UpdateParameters>
<asp:Parameter Type=”DateTime” Name=”DateToShow”></asp:Parameter> <asp:Parameter Type=”String” Name=”Description”></asp:Parameter> <asp:Parameter Type=”String” Name=”PictureURL”></asp:Parameter> <asp:Parameter Type=”String” Name=”Category”></asp:Parameter> <asp:Parameter Type=”String” Name=”Title”></asp:Parameter> <asp:Parameter Type=”Int32” Name=”NewsID”></asp:Parameter>
</UpdateParameters>
<SelectParameters>
<asp:ControlParameter Name=”NewsID” Type=”Int32” ControlID=”GridView1” PropertyName=”SelectedValue”></asp:ControlParameter> </SelectParameters>
<InsertParameters>
<asp:Parameter Type=”DateTime” Name=”DateToShow”></asp:Parameter> <asp:Parameter Type=”String” Name=”Description”></asp:Parameter> <asp:Parameter Type=”String” Name=”PictureURL”></asp:Parameter> <asp:Parameter Type=”String” Name=”Category”></asp:Parameter> <asp:Parameter Type=”String” Name=”Title”></asp:Parameter>
</InsertParameters>
When you’re converting these queries to stored procedures, the parameters can remain the same, because the stored procedures will still require information to be passed into them. So in the next Try It Out, you convert this page and see how the parameters and other queries work.
Try It Out |
Modifying Data and Parameters |
1.Open the Admin\EditNews.aspx page and find the SqlDataSource2 control.
2.In the Database Explorer, expand the WroxUnited.mdf file and add a new stored procedure (select the Stored Procedures item in the Database Explorer and from the right mouse menu select Add New Stored Procedure).
3.Delete the existing contents of the new stored procedure and replace them with the following:
CREATE PROCEDURE dbo.usp_NewsByID @NewsID int
AS
SELECT * FROM News WHERE NewsID = @NewsID
You can copy and paste the SQL from EditNews.aspx if you like. If copying the SQL, you don’t have to worry about the square brackets — these were placed around column names when the data source control was created and ensure that the column names are not interpreted as keywords. Because none of the columns in this procedure are the same as keywords, it doesn’t matter if you have the square brackets or not.
4.Save and close the procedure.
533

Chapter 14
5.Create another new procedure, replacing the default text with the following code:
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
6.Save and close the procedure.
7.Create another new procedure, replacing the default text with the following code:
CREATE PROCEDURE dbo.usp_NewsInsert @DateToShow datetime, @Description text,
@PictureUrl varchar(50), @Category varchar(50), @Title varchar(50)
AS
INSERT INTO News(DateToShow, Description, PictureUrl, Category, Title) VALUES (@DateToShow, @Description, @PictureUrl, @Category, @Title)
8.Save and close the procedure.
9.Create another new procedure, replacing the default text with the following:
CREATE PROCEDURE dbo.usp_NewsDelete @NewsID int
AS
DELETE FROM News WHERE NewsID = @NewsID
10.Save and close the procedure.
11.In the Database Explorer, close the database connection by right-clicking WroxUnited.mdf and selecting the Close Connection menu item.
12.In EditNews.aspx change the SQL commands to their appropriate stored procedures, using the following table as a guide.
Command |
Stored Procedure |
|
|
SelectCommand |
usp_NewsByID |
UpdateCommand |
usp_NewsUpdate |
InsertCommand |
usp_NewsInsert |
DeleteCommand |
usp_NewsDelete |
|
|
534

Performance
13.Add the following attributes to SqlDataSource2:
SelectCommandType=”StoredProcedure”
UpdateCommandType=”StoredProcedure”
InsertCommandType=”StoredProcedure”
DeleteCommandType=”StoredProcedure”
14.Save the page and run the application to prove that the page still works as expected. (like the previous example, you won’t see anything different when running the application). To edit the news items, you’ll have to be a user in one of the following roles: Reporter, Owner, Manager, or Admin. You can find out login details by using the “help” link on the login box when not logged in to the site, but you can use dave, dan, jim, chrish, chrisu, or john.
How It Works
From the first example you can see that the SQL statements in the data source control have been replaced by names of stored procedures, and the appropriate CommandType attributes are set to indicate this. The stored procedures are different from the one used in the first example, though, so take a look at some of the specifics, starting with the one to fetch a row to be edited:
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
535

Chapter 14
UPDATE News
SET DateToShow = @DateToShow, Description = @Description,
PictureUrl = @PictureUrl, Category = @Category,
Title = @Title
WHERE NewsID = @NewsID
There are more parameters here, each 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). 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, char, which 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 automatically added when the string was inserted into the database and would remain when the data is fetched, which means you might have to truncate your data when displaying it. 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 cover in this book. The important thing to remember is that using stored procedures improves performance as well as centralizing data access statements.
For more on SQL, get a copy 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 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:
using System.Collections;
ArrayList myList = new ArrayList(); myList.Add(“abc”); myList.Add(“def”);
String s = (string)myList[0];
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 you’re fetching data from the list, it needs to be converted back to its native type. This conversion between types is an overhead.
536

Performance
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:
using System.Collections.Generic;
List<string> myList = new List<string>; myList.Add(“abc”);
myList.Add(“def”);
string s = myList[0];
When declaring the List, you set the type of object it contains within angle brackets — 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 you’re reading a value from the list. As a result, there’s a performance improvement when adding data and fetching it.
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 been 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” />
537