By avoiding the following 10 common ASP. NET defects, the website can run smoothly.

Source: Internet
Author: User
LoadControl and output Cache
Session and output Cache
Forms authentication ticket lifetime
View status: silent performance killer
SQL Server session Status: Another performance killer
Uncached role
Serialization of configuration file attributes
Thread Pool saturation
Simulate and ACL authorization
Do not trust it completely-set the database configuration file!

One of the reasons for ASP. NET's success is that it lowers the threshold for Web developers. Even if you are not a doctor of computer science, you can write ASP. NET code. Many ASP. NET developers I met at work are self-taught. They are all writing Microsoft Excel workbooks before C # Or Visual Basic. Now they are writing Web applications. In general, their work is commendable.

However, it is still the responsibility that comes with the capabilities. Even experienced ASP. NET developers will inevitably make mistakes. After years of consulting on ASP. NET projects, I found that some errors are especially prone to frequent defects. Some errors may affect the performance. Other errors will suppress scalability. Some errors may also make the development team spend valuable time tracking errors and unexpected behaviors.

The following are the 10 defects that may cause problems during ASP. NET production application release and the methods to avoid them. All examples come from my personal experience in building real Web applications for real companies. In some cases, I will introduce ASP.. NET development team in the development process encountered some issues to provide the relevant background.

LoadControl and output Cache

There are very few ASP. NET applications that do not use user controls. Before the master page appears, developers use user controls to extract public content, such as headers and footers. Even in ASP. in. NET 2.0, user controls also provide effective methods to encapsulate content and behaviors and divide pages into multiple regions, the cache capabilities of these regions can be controlled independently as a whole page (a special form of output cache called segment cache ).

User Controls can be declared or forcibly loaded. Forced loading depends on Page. LoadControl, which instantiates the user control and returns the control reference. If a user control contains a user-defined member (for example, a public attribute), you can convert the reference and access the User-Defined member from your code. The user control in Figure 1 implements the BackColor attribute. Run the following code to load the user control and assign a value to BackColor:

Protected void Page_Load (object sender, EventArgs e) {// load the user Control and add it to the page control Control = LoadControl ("~ /MyUserControl. ascx "); PlaceHolder1.Controls. Add (control); // set its background Color (MyUserControl) control). BackColor = Color. Yellow ;}

The above code is actually very simple, but it is a trap for careless developers to fall. Can you find out the flaws?

If you guess this problem is related to the output cache, you are correct. As you can see, the above code sample compilation and running are normal, but if you try to add the following statement (completely legal) to MyUserControl. ascx:

<%@ OutputCache Duration="5" VaryByParam="None" %>

When you run this page next time, you will see InvalidCastException (oh joy !) And the following error messages:

"The object of the type 'System. Web. UI. partialcachingcontrol' cannot be converted to the type 'myusercontrol '."

Therefore, this code runs normally without the OutputCache command, but an error occurs if the OutputCache command is added. ASP. NET should not run in this way. Pages (and controls) should be unknown for the output cache. So what does this mean?

The problem is that when the output cache is enabled for the user control, LoadControl no longer returns a reference to the control instance; instead, it returns a reference to the PartialCachingControl instance, and PartialCachingControl may or may not wrap the control instance, depends on whether the control output is cached. Therefore, if developers call LoadControl to dynamically load user controls and convert control references to access control-specific methods and properties, they must pay attention to this operation, so that the code can run regardless of whether it has an OutputCache command.

Figure 2 shows the correct method for dynamically loading the user control and converting the reference of the returned control. The following is a summary of its working principles:

If the ASCX file lacks the OutputCache command, LoadControl returns a reference to MyUserControl. Page_Load converts the reference to MyUserControl and sets the BackColor attribute of the control.

If the ASCX file contains an OutputCache command and the control output is not cached, LoadControl returns a reference to PartialCachingControl. The CachedControl attribute of this PartialCachingControl contains references to the basic MyUserControl. Page_Load converts PartialCachingControl. CachedControl to MyUserControl and sets the BackColor attribute of the control.

If the ASCX file contains an OutputCache command and the control output is cached, LoadControl returns a reference to PartialCachingControl (whose CachedControl attribute is null. Note that Page_Load does not continue the operation. The BackColor attribute of the control cannot be set because the output of the control comes from the output cache. In other words, there is no MyUserControl to set the attribute.

No matter whether the. ascx file contains the OutputCache command, the code in Figure 2 runs. Although it looks complicated, it can avoid annoying errors. Simplicity does not always mean ease of maintenance.

Session and output Cache

When it comes to output caching, both ASP. NET 1.1 and ASP. NET 2.0 have a potential problem that affects output caching pages on servers running on Windows Server 2003 and IIS 6.0. I have seen this problem twice on the ASP. NET production server, both of which are solved by disabling the output buffer. Later, I learned that there is a better solution than disabling the output cache. The following is the first time I encountered this problem.

At that time, the situation was like this: a website (called Contoso.com here, which is in a small ASP.. NET Web applications). I contacted my team and complained that they encountered a "cross-Thread" error. Customers who use the Contoso.com website often suddenly lose the input data, but see the data of another user. After a slight analysis, we found that the cross-thread description is not accurate; the "cross-session" error is more appropriate. It seems that Contoso.com stores data in the session state. For some reason, users occasionally connect to sessions of other users randomly.

One of my team members has compiled a diagnostic tool to record key elements (including Cookie headers) of each HTTP request and response to the log. Then, he installed the tool on the Contoso.com Web server and made it run for several days. The results are very obvious. About once in every 100000 requests: ASP. NET correctly assigns a session ID for the new session and returns the session ID in the Set-Cookie header. Then, it will return the same session ID (that is, the same Set-Cookie header) in the next closely adjacent request ), even if the request has been associated with a valid session and the session ID in the Cookie is correctly submitted. In fact, ASP. NET randomly switches users from their own sessions and connects them to other sessions.

We were surprised, So we began to look for reasons. First, we checked the source code of Contoso.com. We are pleased that the problem is not there. Then, to ensure that the problem has nothing to do with the Web domain of the application host, we keep only one server running and close all other servers. The problem still exists, which is not surprising because our log shows that the matched Set-Cookie header will never come from two different servers. ASP. NET accidentally generated a duplicate session ID, which is incredible because it uses. NET Framework RNGCryptoServiceProvider class generates these IDs, and the session ID length is enough to ensure that the same ID will never be generated twice (at least twice in the next 1 trillion years ). In addition, even if the RNGCryptoServiceProvider mistakenly generates duplicate random numbers, it cannot explain why ASP. NET unexpectedly replaces the Valid Session ID with a new one (not unique ).

Intuitively, we decided to look at the output cache. When OutputCacheModule caches HTTP responses, it must be careful not to cache the Set-Cookie header; otherwise, the cache response containing the new session ID connects all the recipients of the cache response (and the users whose requests generate the cache response) to the same session. We checked the source code; Contoso.com enabled the output cache on both pages. We disabled the output cache. As a result, the application runs for several days without a cross-session problem. Since then, it has been running for more than two years without any errors. In another company with different applications and a set of different Web servers, we can see that the same problem also disappears. Just like in Contoso.com, eliminating the output cache can solve the problem.

Microsoft later confirmed that this behavior originated from an issue in OutputCacheModule. (An update may have been released when you read this article .) When ASP. NET and IIS 6.0 are used together and kernel mode cache is enabled, the OutputCacheModule sometimes cannot delete the Set-Cookie header from the cache response that it passes to Http. sys. The specific sequence of events that result in errors is as follows:

A user who has not recently accessed a website (and thus has no corresponding session) requests a page with the output cache enabled, but its output is currently unavailable in the cache.

This request executes the code used to access the user's newly created session, resulting in the session ID Cookie being returned in the response's Set-Cookie header.

OutputCacheModule provides output to Http. sys, but cannot delete the Set-Cookie header from the response.

Http. sys returns a cache response in subsequent requests and mistakenly connects other users to the session.

What is the meaning of the story? The session Status and kernel mode output cache cannot be used together. If you use session status on the page that enables the output cache and the application runs on IIS 6.0, You need to disable kernel mode output cache. You will still benefit from the output cache, but because the output cache in kernel mode is much faster than the normal output cache, the cache will not be equally effective. For more information, see support.microsoft.com/kb/917072.

You can disable the kernel mode output cache for a single page by using the OutputCache command on the page containing VaryByParam = "*" attribute, although this may result in a sudden increase in memory requirements. Another safer method is to disable the kernel-mode cache of the entire application by including the following elements in web. config:

You can also use registry settings to globally disable the kernel-mode output cache, that is, disable the kernel-mode output cache of all servers. For more information, see support.microsoft.com/kb/820129.

Every time I hear the customer report session has a puzzling problem, I will ask them if they have used the output cache on any page. If the output cache is used and the host operating system is Windows Server 2003, I recommend that you disable the output cache in kernel mode. The problem is usually solved. If the problem persists, the error exists in the code. Be alert!

Forms authentication ticket lifetime

Can you find out the following code problems?

FormsAuthentication.RedirectFromLoginPage(username, true);

This Code seems to be okay, but it cannot be used in ASP. NET 1.x applications unless code elsewhere in the application offsets the negative effect of this statement. If you cannot determine the reason, continue reading.

FormsAuthentication. RedirectFromLoginPage executes two tasks. First, when FormsAuthenticationModule redirects the user to the logon page, FormsAuthentication. RedirectFromLoginPage redirects the user to the page they originally requested. Second, it publishes an authentication ticket (usually carried in a Cookie and in ASP. NET 1.x is always carried in the Cookie). This ticket allows the user to maintain the authentication status for a specified period of time.

The problem lies in this time period. In ASP. NET 1.x, passing another parameter false to RedirectFromLoginPage will issue a temporary authentication ticket, which will expire 30 minutes by default. (You can use the Timeout attribute in the web. config element to change the Timeout period .) However, if another parameter is set to true, a permanent authentication ticket is issued, which is valid for 50 years! In this case, a problem occurs because if someone steals the authentication ticket, they can use the identity of the victim to access the website within the validity period of the ticket. There are multiple ways to steal authentication tickets-detecting unencrypted communication at public wireless access points, writing scripts across websites, physically accessing the victim's computer, and so on-therefore, passing true to RedirectFromLoginPage is less secure than disabling your website. Fortunately, this problem has been solved in ASP. NET 2.0. The current RedirectFromLoginPage accepts the timeout value specified for the temporary and permanent authentication ticket in web. config in the same way.

One solution is to never pass true in the second parameter of RedirectFromLoginPage of ASP. NET 1.x application. However, this is not practical, because the logon page usually contains a "Keep me as login" box. You can select this box to receive a Cookie for permanent authentication instead of temporary authentication. Another solution is to use Global. the code segment in asax (you can also use the HTTP module if you want), which is modified before the Cookie containing the permanent authentication ticket is returned to the browser.

Figure 3 contains a code segment. If the code snippet is located in Global. asax, it modifies the Expires attribute of the Cookie that passes permanent Forms authentication so that the Cookie Expires after 24 hours. You can set the timeout value to any date you like by modifying the line annotated with "new expiration date.

You may find it strange that the Application_EndRequest method calls the local Helper method (GetCookieFromResponse) to check the outgoing response of the authentication Cookie. The Helper method is another way to solve ASP. NET 1.1 errors. If you use the string index generator of HttpCookieCollection to check for non-existent cookies, this error will cause the false cookies to be added to the response. Using the integer index generator as GetCookieFromResponse can solve this problem.

View status: silent performance killer

In a sense, view State is the greatest thing in history. After all, the view status enables the page and control to remain in the status between sending and receiving. Therefore, you do not need to write code like in traditional ASP to prevent text in the text box from disappearing when you click the button, or re-query the database and re-bind the DataGrid after sending the response.

However, the view status also has its disadvantages: when it grows too large, it becomes a silent performance killer. Some controls (such as text boxes) determine the view status accordingly. Other controls (especially the DataGrid and GridView) determine the view status based on the displayed amount of information. If the GridView displays 200 or 300 rows of data, I will be daunting. Even if ASP. NET 2.0 view status is ASP. NET 1 x View status is half the size, a bad GridView can also easily reduce the effective bandwidth of the connection between the browser and the Web server by 50% or more.

You can disable the view status of a single control by setting EnableViewState to false, but some controls (especially DataGrid) lose some functionality when they cannot use view status. A better solution to controlling view status is to keep it on the server. In ASP. NET 1.x, You can override the LoadPageStateFromPersistenceMedium and SavePageStateToPersistenceMedium methods of the page and process the view status as you like. The code shown in Figure 4 is rewritten to prevent the view status from being retained in the hidden field while retaining it in the session state. When used together with the default session state process model (that is, when the session state is stored in the ASP. NET auxiliary process in the memory), the view State is particularly effective in the session state. On the contrary, if the session status is stored in the database, only the test can be displayed in the session status. Retaining the view status will improve or degrade the performance.

ASP. NET 2.0 uses the same method, but ASP. NET 2.0 provides a simpler way to keep the view State in the session state. First, define a custom page Adapter. The GetStatePersister method returns an instance of the. NET Framework SessionPageStatePersister class:

public class SessionPageStateAdapter :System.Web.UI.Adapters.PageAdapter{public override PageStatePersister GetStatePersister (){return new SessionPageStatePersister(this.Page);}}

Then, add the App. browsers file to the App_Browsers folder of the application in the following way, and register the custom page adapter as the homepage page adapter:

<browsers><browser refID="Default"><controlAdapters><adapter controlType="System.Web.UI.Page"adapterType="SessionPageStateAdapter" /></controlAdapters></browser></browsers>

(You can name a file as long as its extension is. browsers .) ASP. NET then loads the page adapter and uses the returned SessionPageStatePersister to retain all page states, including view states.

One disadvantage of using a custom page adapter is that it acts globally on every page of the application. If you prefer to retain the view status of some pages in the session state rather than those of other pages, use the method shown in Figure 4. In addition, if you create multiple browser windows in the same session, you may encounter problems using this method.

SQL Server session Status: Another performance killer

ASP. NET makes it easy to store session states in databases: you only need to switch the switch in web. config to easily move the session status to the backend database. This is an important feature for applications running in the Web field because it allows each server in the field to share a public library of session states. The added database activity reduces the performance of a single request, but the increase in scalability makes up for the performance loss.

This looks pretty good, but it may be different if you consider the following:

Even in applications that use session Status, most pages do not use session status.

By default, the ASP. NET session Status manager performs two accesses (one read access and one write access) to the session data storage in each request, regardless of whether the requested page uses the session status.

In other words, when you use the SQL Server session status option, you have to pay a price for each request (two database accesses)-even in requests on pages unrelated to the session status. This directly affects the throughput of the entire website.

Figure 5 remove unnecessary session State database access

What should you do? Simple: Disable the session status on the page that does not use the session status. This is always a good method, but this method is especially important when the session status is stored in the database. Figure 5 shows how to disable the session status. If the Page does not use the session status at all, include EnableSessionState = "false" in its Page Command, as shown below:

<%@ Page EnableSessionState="false" ... %>

This command prevents the session Status manager from reading and writing session Status databases in each request. If the page reads data from the session status but does not write data (that is, the content of the user session is not modified), set EnableSessionState to ReadOnly, as shown below:

<%@ Page EnableSessionState="ReadOnly" ... %>

Finally, if the page requires read/write access to the session status, the EnableSessionState attribute is omitted or set to true:

<%@ Page EnableSessionState="true" ... %>

By controlling the session status in this way, you can ensure that ASP. NET only accesses the session status database when it is actually needed. Eliminating unnecessary database access is the first step in building high-performance applications.

By the way, the EnableSessionState attribute is public. This attribute has been described since ASP. NET 1.0, but I still seldom see developers using this attribute. It may be because it is not very important for the default session state model in the memory. However, it is very important for the SQL Server model.

Uncached role

The following statements are often used in the web. config file of ASP. NET 2.0 applications and an example of ASP. NET 2.0 role Manager:

<roleManager enabled="true" />

But as shown above, this statement does have a significant negative impact on performance. Do you know why?

By default, ASP. NET 2.0 role manager does not cache role data. On the contrary, it will refer to role data storage every time it needs to determine which role (if any) the user belongs. This means that once the user has been authenticated, any page that uses role data (for example, a page that uses a website graph with security cut settings enabled, and a page that uses web. in config, the restricted page is accessed Based on the role URL Command.) This will cause the role manager to query the role data storage. If the role is stored in the database, you can easily avoid accessing multiple databases when each request needs to access multiple databases. The solution is to configure the role manager to cache role data in cookies:

<roleManager enabled="true" cacheRolesInCookie="true" />

You can use other <roleManager> attributes to control the features of the role Cookie-for example, the Cookie should be valid for a long time (and the frequency at which the role manager returns the role database ). By default, role cookies are signed and encrypted. Therefore, although the security risk is not zero, it is mitigated.

Serialization of configuration file attributes

The ASP. NET 2.0 configuration file service provides a ready-to-use solution for maintaining the status of each user (such as personalized preferences and language preferences. To use the configuration file service, you can define an XML configuration file that contains the attributes representing a single user to be retained. Then, ASP. NET compiles a class containing the same attributes and provides strong type access to the class instance by adding the configuration file attributes to the page.

The configuration file is highly flexible. It can even use custom data types as configuration file attributes. However, one problem exists. I have seen this problem with my own eyes, which leads to a developer error on a business trip. Figure 6 contains a simple class named Posts and the configuration file definition that uses Posts as the configuration file property. However, this class and the configuration file will generate unexpected behavior at runtime. Can you find out the cause?

The problem is that Posts contains a private field named _ count, which must be serialized and deserialized to completely freeze and refreeze class instances. However, _ count is not serialized or deserialized because it is private. By default, the ASP. NET Configuration File Manager uses XML serialization to serialize and deserialize custom types. The XML serialization program ignores non-public members. Therefore, the Posts instance will be serialized and deserialized, but each time the deserialization class instance, _ count will be reset to 0.

One solution is to make _ count a public field rather than a private field. Another solution is to encapsulate _ count using public read/write attributes. The best solution is to mark Posts as SerializableAttribute and configure the configuration file manager to serialize and deserialize class instances using the. NET Framework binary serialization program. This solution can maintain the design of the class itself. Different from the XML serialization program, the binary serialization program serializes fields regardless of whether they can be accessed. Figure 7 shows the fixed version of the Posts class and the configuration file definition that is added to the change.

Note that if you use a custom data type as the configuration file property and the data type has non-public data members that must be serialized to fully serialize the type instance, use the serializeAs = "Binary" attribute in the attribute Declaration and ensure that the type itself is serializable. Otherwise, you will not be able to complete serialization, And you will waste time trying to determine why the configuration file cannot work.

Thread Pool saturation

When I perform a database query and wait for 15 seconds or longer to obtain the returned query results, I am often surprised by the actual ASP. NET page number. (I also waited 15 minutes to see the query results !) Sometimes, latency is inevitable because of the large amount of returned data. Sometimes, latency is caused by poor database design. However, for whatever reason, a long database query or any type of long I/O operations may cause a decrease in throughput in ASP. NET applications.

I have described this issue in detail before, so I will not explain it too much here. I can only say one thing, ASP. NET depends on a limited thread pool to process requests. If all threads are occupied to wait for database queries, Web service calls, or other I/O operations to complete, before an operation is completed and a thread is released, other requests must wait in queue. When requests are queued, the Performance drops sharply. If the queue is full, ASP. NET will cause subsequent requests to fail with an HTTP 503 error. This is not what we want to see in the production applications of Web production servers.

The solution does not belong to asynchronous pages, which is one of the best but little-known functions in ASP. NET 2.0. An asynchronous page request starts from a thread, but when it starts an I/O operation, it returns the thread and the IAsyncResult interface of ASP. NET. After the operation is complete, the request uses IAsyncResult to notify ASP. NET that ASP. NET extracts another thread from the pool and processes the request. It is worth noting that when I/O operations occur, the thread pool thread is not occupied. In this way, you can significantly increase the throughput by blocking requests from other pages (pages without long I/O operations) waiting in the queue.

You can read all information about asynchronous pages in the October 2005 MSDNMagazine journal. I/O binding is likely to become an asynchronous page for any page that requires a long execution time instead of a computer binding.

When I tell developers about asynchronous pages, they often say, "That's great, but I don't need them in my applications ." I replied, "Do You Need to query databases on any page? Do they call Web Services? Have you checked statistics on queuing requests and average waiting time in ASP. NET performance counters? Even if your application runs normally so far, the load of your application may increase as your customer grows ."

In fact, most actual ASP. NET applications require asynchronous pages. Please remember this!

Simulate and ACL authorization

The following is a simple configuration command, but every time I see it in web. config, it makes me look bright:

<identity impersonate="true" />

This command enables client simulation in ASP. NET applications. It attaches the access token representing the client to the thread that processes the request, so that the security check executed by the operating system targets the client identity rather than the auxiliary Process Identity. Few ASP. NET applications need to be simulated. My experience tells me that developers often enable Simulation for errors. The reasons are as follows.

Developers often enable simulation in ASP. NET applications so that they can use file system permissions to restrict access to pages. If Bob does not view Salaries. aspx permission, the developer will enable the simulation, so that by setting the access control list (ACL) to deny Bob's read permission, prevent Bob from viewing Salaries. aspx. However, the following risks exist: simulation is unnecessary for ACL authorization. When Windows authentication is enabled in an ASP. NET application, ASP. NET automatically checks the ACL for each. aspx page of the request and rejects the requests from callers who do not have the permission to read files. Even if simulation is disabled, it still performs this operation.

Sometimes it is necessary to prove the rationality of the simulation. However, you can avoid it with good design. For example, assume that Salaries. aspx queries the salary information that only Administrators can know in the database. Through simulation, you can use the database permission to reject the ability of non-administrators to query wage data. Alternatively, you can set an ACL for Salaries. aspx so that non-administrators do not have the read permission, thus limiting access to wage data. The latter method provides better performance because it completely avoids simulation. It also eliminates unnecessary database access. Why is the query database rejected only for security reasons?

By the way, I have helped to troubleshoot a traditional ASP application, which restarts on a regular basis due to unlimited memory usage. An inexperienced developer converts the target SELECT statement to SELECT * without considering that the table to be queried contains images, which are large and large. The problem is caused by memory leakage not detected. (My managed code field !) Applications that run normally over the years have suddenly stopped working because the SELECT statement that previously returned 1000 or 2000 bytes of data now returns several megabytes. With inadequate version control, the development team's life will have to be "excited"-the so-called "excited" here is like when you are going to bed at night, you have to watch your child play the same boring football game.

Theoretically, traditional memory leaks do not occur in ASP. NET applications that are fully composed of hosted code. However, insufficient memory usage will affect performance by forcing garbage collection to happen more frequently. Even in ASP. NET applications, be cautious with SELECT *!

Do not trust it completely-set the database configuration file!

As a consultant, I was often asked why the application was not executed as expected. Recently, someone asked my team why ASP. NET applications only process about 1/100 of the throughput required for the request documentation (requests per second. What we have previously discovered is the unique problem we found in a Web application that cannot run normally-and the lessons we should all take seriously.

We run SQL Server Profiler and monitor the interaction between the application and the backend database. In a more extreme case, only one button is clicked, resulting in more than 1,500 database errors. You cannot build high-performance applications in that way. A good architecture always begins with a good database design. No matter how efficient your code is, it does not work if it is dragged down by poorly written databases.

The Bad Data Access architecture usually comes from one or more of the following aspects:

Poor Database Design (usually designed by developers rather than database administrators ).

DataSets and DataAdapters, especially DataAdapter. Update, are applicable to Windows Forms applications and other fat clients, but are generally not ideal for Web applications.

A poorly designed data access layer (DAL) with poor compilation of computing programs and relatively simple operations that consume many CPU cycles ).

You must confirm the problem before you can handle it. To identify data access problems, run SQL Server Profiler or an equivalent tool to view the operations being performed in the background. Check the communication between the application and the database before the performance adjustment is completed. Try-you may be surprised by your findings.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.