Performance Tuning Tips
Performance tuning involves fixing bottlenecks and tweaking code to achieve your desired throughput rate or response time criteria while maintaining scalability. Using new features in ASP.NET, such as caching and new data access
Application and Session State
Maintaining state without creating performance and scalability problems in a Web application distributed among multiple Web servers proved to be challenging in the past. There are more options available for ASP.NET Web applications compared to traditional ASP Web applications, but you still must be aware of the performance versus scalability tradeoffs for each option.
Application State
Traditionally application variables were used to store information like connection strings or as a caching mechanism for storing
Session State
Session information is data stored in memory of the Web server for each
ASP.NET addresses some of the scalability issues previously associated with using session data in Web farms by offering the option to store it out-of-process in a Windows service or to store it in a SQL server. Keep in mind that scalability is
Caching in ASP.NET
Caching has been greatly improved with ASP.NET and when used properly can boost application performance significantly. With traditional ASP, caching was implemented by either storing all of your data in session variables, application variables, or by using a custom caching solution. These methods are still available in ASP.NET, but ASP.NET has even more options available to the developer. With the new caching mechanisms the output of entire pages can be cached via a simple directive. Additionally, there is an advanced caching engine and a caching API that can be used to store any arbitrary piece of information that will be reused often.
NOTE
We recommend output caching frequently accessed pages in your ASP.NET Web application whenever possible, but you should always follow up your tuning efforts with testing. Be careful not to go overboard because caching too much data can use
Output Caching
The ASP.NET output cache can use memory on the server to store the output of
This feature can easily be enabled for pages by including the OutputCache directive within the page. For example, to save the output of a processed page for a maximum of 60 seconds, using the most basic syntax, you can include the following directive in the page:
<%@ OutputCache Duration="60" VaryByParam="None"%>
The Duration and VaryByParam attributes are required.
NOTE
It is recommended that pages which are output cached have a
Duration
of at least 60 seconds, or the turnover rate of the page may hinder rather than benefit performance.
For pages that are short lived but have
<%@ OutputCache Duration="10" VaryByParam="ProductCategory;Product"%>
Besides the two required attributes that are supported by the OutputCache directive, there are three additional attributes all of which are optional; these are Location , VaryByCustom, and VaryByHeader . The Location attribute controls where the data will be cached (for example, the server or client). VaryByHeader can cache based on specific headers sent with the request, and VarybyCustom can be used to cache based on browser type when specified with a value of Browser or can be used to implement custom logic when supplied with any other value.
Fragment Caching
Fragment caching is similar to output caching in the sense that the directive is the same. This level of caching is used to cache portions of a page that are implemented as user controls and is also referred to as partial page caching or user controls caching. Fragment caching should be
The Output directive to implement fragment caching has to be included as part of the file implementing the control. The Duration and VaryByParam attributes are required and are exactly the same as in output caching. Additionally, there is the VaryByControl attribute, which is specific to fragment caching and can be included only in the user control file.
<%@ OutputCache Duration="60" VaryByParam="None"%>
Caching API
The caching API lets you save any piece of information in server memory that you want to reuse. For example, let us say that you need to display the product categories on a page in addition to other information. Rather than retrieving this information from the database with every request to the page, you can save the categories via the caching API. The most basic syntax to cache something is:
Cache("mydata"}() = "some data"
You can store entire data sets besides just strings and numeric data. Retrieving the cached data is just as simple:
X = Cache("mydata")
Other useful methods to be aware of are the Remove method, used to remove an item from the cache and the Insert method to add items to the cache. The syntax for the Remove method is:
(Cache.Remove("mydata"))
The Insert method is an overloaded method of the Cache object and has several versions. For example, the following version of the Insert method can be used to add an item to the cache with no external dependency and with an absolute expiration time of 120 minutes from the first time the page is cached:
Cache.Insert("mydata", mydata, nothing, _ DateTime.Now.AddMinutes(120), TimeSpan.Zero)
The last parameter in the previous example is known as the sliding window and can be used to set an expiration for a cached item relative to the time the item was first placed in or last retrieved from the cache The sliding value parameter can be thought of as the maximum length of time between successive calls that need to elapse before a cached item is removed from the cache. For example, to place an item in the cache for a maximum of 10 minutes between successive retrievals, you can use the following syntax of the Cache object s Insert method:
Cache.Insert("mydata", mydata, nothing, DateTime.MaxValue, _ TimeSpan.FromMinutes(10))
Disabling ViewState
ViewState saves the properties from one page (usually from a form) to the
{% if main.adsdop %}{% include 'adsenceinline.tpl' %}{% endif %}
ADO.NET Tips
Most Web applications are built with a back-end database management system. Connecting to this data
The .NET Framework ships with two .NET data providers: The OLE DB .NET Data Provider and the SQL Server .NET Data Provider. The OLE DB .NET Data Provider can be used to connect to any data source for which there exists an OLE DB Provider, for example Microsoft SQL Server or an Oracle database, but is primarily intended for non-SQL Server databases. For applications that use Microsoft SQL Server versions 7.0 or higher, the SQL Server .NET Data Provider is the better choice. This provider has been optimized
NOTE
Use the SQL Server .NET Data Provider with SQL Server versions 7.0 and higher.
SqlConnection Object
The first step in communicating with the data tier is to establish a connection with the database server. The SQL Server .NET Data Provider gives us the
SqlConnection
object for this purpose. Creating a connection is
Dim strCnStr As String = "Data Source =.;" _ & "Integrated Security=SSPI;" _ & "Initial Catalog = Pubs" Dim objCn as New SqlConnection(strCnStr) _ objCn.Open ()
By default, this data provider takes advantage of connection pooling. This helps reduce the overhead of establishing a connection each time it is
NOTE
It is recommended that applications either use integrated security whenever possible or implement a common application username/password that is shared by all users in order to improve the efficiency of the connection pooling usage.
The other factor that determines whether a pooled connection is utilized is the transaction context. A second connection will use a pooled connection as long as the transaction context is the same as the initial connection or does not have one at all.
Controlling the size of the connection pool is affected by specifying the
min
and
max
properties. This is important if you need to control the amount of memory utilized at the Web tier. If all pooled connections are active, any extra connection request will be blocked until one is relinquished or the connection time out has
Dim strCnStr As String = "Data Source =.;" _ & "Integrated Security=SSPI;" _ & "Initial Catalog = Pubs;" _ & "Min Pool Size=10;" _ & "Max Pool Size =100"
Another property that can have an effect on performance is the packet size. For applications that transfer large blob or image fields, increasing the packet size can be beneficial. In cases where the amount of data transferred is small, a smaller value for the packet size may be more efficient. The following code demonstrates setting this property as part of the connection string:
Dim strCnStr As String = "Data Source =.;" _ & "Integrated Security=SSPI;" _ & "Initial Catalog = Pubs;" _ & "Packet Size=32768"
SqlCommand Object
A common scenario for Web applications is the retrieval/modification of data from the data source. The SQL Server .NET provider implements the
SqlCommand/DataReader
and the
DataAdapter /DataSet
classes that allow the user to retrieve/modify the data. We only
The
SqlCommand/DataReader
is connection oriented and provides certain methods that can be leveraged to improve application performance. These methods include the
ExecuteNonQuery
,
ExecuteScalar
and
ExecuteReader
. Additionally the
SqlCommand
class implements the
ExecuteXmlReader
method for data returned in XML format. A description of these four methods along with an example (VB.NET Console application) for each
ExecuteNonQuery Method
This method is typically used with Insert, Update, and Delete operations. The only piece of information that is most useful in these cases, and that is returned to the client, is the number of rows that are affected. This method will also work with stored procedures that contain output/return parameters, which can be returned to the client. The following Visual Basic .NET code demonstrates this method by calling a stored procedure that returns a count of the number of customers in the Customer table in the NorthWind database as a return value:
Imports System Imports System.Data Imports System.Data.SqlClient Module ExecuteNonQuery Sub Main() Dim strConnString As String = "Data Source=.;" _ & "Initial Catalog=Northwind;" _ & "Integrated Security=SSPI" Dim strSQL As String = "GetNumberOfCustomers" Dim sqlConn As New SqlConnection(strConnString) Dim sqlComd As New SqlCommand(strSQL, sqlConn) sqlComd.CommandType = CommandType.StoredProcedure sqlComd.Parameters.Add(New _ SqlParameter("@i", SqlDbType.Int)) sqlComd.Parameters(0).Direction = _ ParameterDirection.ReturnValue sqlConn.Open() sqlComd.ExecuteNonQuery() sqlConn.Close() Console.WriteLine("Number of customers = {0}", _ CType(sqlComd.Parameters(0).Value, Integer)) End Sub End Module
ExecuteScalar Method
This method should be used whenever you need to retrieve a single value from the data tier, for example, if you need a count of customers or the customer ID of a single customer. To
Imports System Imports System.Data Imports System.Data.SqlClient Module ExecuteScalar Sub Main() Dim strConnString As String = "Data Source=.;" _ & "Initial Catalog=Northwind;" _ & "Integrated Security=SSPI" Dim strSQL As String = "select count(*) from customers" Dim sqlConn As New SqlConnection(strConnString) Dim sqlComd As New SqlCommand(strSQL, sqlConn) sqlConn.Open() Dim o As Object= sqlComd.ExecuteScalar() sqlConn.Close() Console.WriteLine("Number of customers = {0}", _ CType(o, Integer)) End Sub End Module
ExecuteReader Method
Any time you need to return a single data row or multiple data rows containing a lot of
Imports System Imports System.Data Imports System.Data.SqlClient Imports Microsoft.VisualBasic Module ExecuteReader Sub Main() Dim strConnString As String = "Data Source=.;" _ & "Initial Catalog=Northwind;" _ & "Integrated Security=SSPI" Dim strSQL As String = _ "select customerid,contactname,phone from customers" Dim sqlConn As New SqlConnection(strConnString) Dim sqlComd As New SqlCommand(strSQL, sqlConn) sqlConn.Open() Dim sqlDR As SqlDataReader = _ sqlComd.ExecuteReader(CommandBehavior.CloseConnection) Do While sqlDR.Read() Console.WriteLine(sqlDR("
customerid
").ToString() _ & ControlChars.Tab _ & sqlDR.GetSqlString(1).ToString() _ & ControlChars.Tab _ & sqlDR.GetSqlString(2).ToString()) Loop sqlDR.Close() End Sub End Module
In cases where you are sure only one row is returned to the client, you can call this method by supplying the SingleRow value of the Command Behavior enumeration as a parameter. The syntax with this optional parameter is:
Dim sqlDR As SqlDataReader = _ sqlComd.ExecuteReader(CommandBehavior.SingleRow);
Data values in each row can be referenced either by name or ordinal position as
TIP
Try to use the type-specific methods of the SQL Server .NET Provider whenever possible.
ExecuteXMLReader Method
This method is useful when data is returned from SQL Server in XML format. For example, to return the data in XML format, the SQL statement can be modified to instruct SQL Server to return the data in XML format. The example that follows demonstrates using this method:
Imports System Imports System.Data Imports System.Data.SqlClient Imports System.Xml Module ExecuteXmlReader Sub Main() Dim strConnString As String = "Data Source=.;" _ & "Initial Catalog=Northwind;" _ & "Integrated Security=SSPI" Dim strSQL As String = "SELECT customerid," _ & "contactname," _ & "phone " _ & "From customers " _ & "FOR XML AUTO" Dim sqlConn As New SqlConnection(strConnString) Dim sqlComd As New SqlCommand(strSQL, sqlConn) sqlConn.Open() Dim xmlR As XmlReader = sqlComd.ExecuteXmlReader() Do While xmlR.Read() Console.WriteLine(xmlR.ReadOuterXml()) Loop xmlR.Close() sqlConn.Close() End Sub End Module