Web Q&A

Caching Transforms, Connection Sharing, and More

Edited by Nancy Michell

Q I have an ASP.NET application that uses the XML control to transform an XML file (myXML.xml) and XSLT file (myXSLT.xslt) into HMTL. The file, myXML.xml, is the result of two distinct XML documents with different schemas: myStaticXML.xml provides most of the data needed for the output and myWebServiceXML.xml provides key fields needed to complement myStaticXML to arrive at the desired HMTL output.

Q I have an ASP.NET application that uses the XML control to transform an XML file (myXML.xml) and XSLT file (myXSLT.xslt) into HMTL. The file, myXML.xml, is the result of two distinct XML documents with different schemas: myStaticXML.xml provides most of the data needed for the output and myWebServiceXML.xml provides key fields needed to complement myStaticXML to arrive at the desired HMTL output.

When the ASP.NET page loads, I call a Web service that creates myWebService.xml and loads it into a DataSet. Then I use the DataSet, an instance of XmlTextReader, and the DataSet.ReadXml and DataSetMerge methods to get the data and manipulate the fields accordingly. Once I have the combined data, I create myXML.xml and do the transformation.

Is this the best method to accomplish my performance goals?

A The bottom line is that your overall approach is fine, but lots of improvements can be made, specifically using various levels of caching. There are a number of performance implications to think about concerning each object and technique.

A The bottom line is that your overall approach is fine, but lots of improvements can be made, specifically using various levels of caching. There are a number of performance implications to think about concerning each object and technique.

DataSet Loading XML into a DataSet is inherently more expensive than using an XmlDocument because the DataSet typically wants to convert all the leaf XML nodes into data types other than string (like float, date, time, int, and so on). If this is not needed, then you should stop using a DataSet and instead use an XmlDocument. One reason to keep the DataSet is because of the handy merge feature based on XML updategrams. Unfortunately, XmlDocument does not have this feature. But if you know that the merge is always the same, you could code it explicitly using the XmlDocument API and it would probably be faster.

Caching myStaticXml.xml Using another ASP XML control, you can actually cache the myStaticXML.xml source document. Then you can clone this document using the CloneNode method each time you want to edit it and pass that to the real XML control that has a transform associated with it. Alternatively, you can program against the ASP.NET Cache object directly. Cloning should be faster than file I/O and XML parsing.

Caching the call to the Web service If you must call it on every request, you should do this using async completion ports so your ASP.NET thread can return to the thread pool during the time you are waiting for a response. Perhaps hooking this in at the IHttpHandler level would make more sense, as you can then pass the result to the ASP.NET page as a value in the current context.

Caching the output Again, unless you must do a transform on every single client request using ASP.NET output page, caching will help a great deal.

Hybrid approach on output caching using custom XmlWriter Depending on how complex your merge process is, you could potentially do the merge in a custom XmlWriter which you pass to the XslTransform (this means not using the XML control, but writing the transform code yourself). In this case you transform the static cached myStaticXML.xml file, passing in a custom XmlWriter that looks for marker elements in the output and uses those markers to trigger insertion of the variables that need to be customized. This obviously works only when the variables are very simple, like the current user's name and so forth. This approach takes the most work but it would yield a dramatic improvement in performance so this technique is used on sites that have very high scalability requirements like https://my.msn.com.

Q I need to know how to allow two pages to call the same event handler in ASP.NET. Can I define an event handler in a central place? How do I register event handlers declaratively to call it, or do I need to create an event handler that then calls the central code?

Q I need to know how to allow two pages to call the same event handler in ASP.NET. Can I define an event handler in a central place? How do I register event handlers declaratively to call it, or do I need to create an event handler that then calls the central code?

A You could do this by creating a Page-derived class from which all your pages derive. You would define your protected event handlers in this base class and your controls can then declaratively hook up to them. For more information on how Page works and how you can inherit from it, check out Web Forms Code Model.

A You could do this by creating a Page-derived class from which all your pages derive. You would define your protected event handlers in this base class and your controls can then declaratively hook up to them. For more information on how Page works and how you can inherit from it, check out Web Forms Code Model.

Q In the December 2003 issue a reader said he had an application that makes HTTP requests against IIS and wanted to know if he could perform authentication with secure pages (using NTLM). The suggestion was to use HttpWebRequest.UnsafeAuthenticatedConnectionSharing. I need to understand the possible various security threats when using this option since the connection can be shared. Does using the ConnectionGroupName make taking this suggestion secure, and are there other things that I need to be aware of? What are the implications of using this option in a Network Load Balancing (NLB) environment?

Q In the December 2003 issue a reader said he had an application that makes HTTP requests against IIS and wanted to know if he could perform authentication with secure pages (using NTLM). The suggestion was to use HttpWebRequest.UnsafeAuthenticatedConnectionSharing. I need to understand the possible various security threats when using this option since the connection can be shared. Does using the ConnectionGroupName make taking this suggestion secure, and are there other things that I need to be aware of? What are the implications of using this option in a Network Load Balancing (NLB) environment?

A NLB shouldn't factor into this. The notion of UnsafeAuthenticatedConnectionSharing is reuse of an already established HTTP connection, thereby skipping a (re)challenge for credentials on all but the first request. Since the connection remains established, there's no reason NLB should ever direct traffic for that connection to a different server. If a server ever fails, so would the ongoing connection.

A NLB shouldn't factor into this. The notion of UnsafeAuthenticatedConnectionSharing is reuse of an already established HTTP connection, thereby skipping a (re)challenge for credentials on all but the first request. Since the connection remains established, there's no reason NLB should ever direct traffic for that connection to a different server. If a server ever fails, so would the ongoing connection.

The Microsoft® .NET Framework team originally added this feature a few years back, specifically to address NTLM authentication behavior. With the feature off, the HTTP connection is closed after each request/response. The result is that every Web service request is gated by a matching authentication handshake. With the feature on, you get authenticated only once for a series of requests. As long as the connection stays established, the original credentials are assumed by the server. You get better throughput and deftly avoid churning connections.

The potential security exposure comes from the client-side being trusted regarding the appropriate use of the credentials. The server-side implicitly "trusts" that the requests coming on a given connection are to be processed under the same credentials as those provided when the connection was created.

The ServicePoint connection pool (one ServicePoint manages a number of zero-n active connections against a specific URI) that is used for connections on the client side is shared across an AppDomain. Thus, any code running in the AppDomain could potentially hijack existing connections from the right ServicePoint and send its own requests, which would be processed under potentially different credentials.

This isn't really very radical since rogue code could always send requests with the current credentials (barring code-access security prevention) just by making a new HttpWebRequest. The exposure arises if there are connections in the ServicePoint that uses credentials that are not current. There's no real way to prevent any code from being able to grab them. This is most likely part of the reason for the "think twice before using" flavor of the property name. This option can make a huge difference, so it's definitely worth using, although you should do so advisedly.

As for throughput, also worth noting is the ServicePointManager.Expect100Continue Property). With the default value of true, you're paying for an extra round-trip on every request—the headers go on the first trip; the client waits for a server HTTP response before sending the body. Set this to false to get what you were probably expecting, the simultaneous sending of the headers and the body.

Q In Internet Explorer, can I hide the toolbar and menu bar on the current page without using window.open and without having to create a new window?

Q In Internet Explorer, can I hide the toolbar and menu bar on the current page without using window.open and without having to create a new window?

A You'll probably find that some users have opened Internet Explorer and set the options to show the toolbar and menu bar. Many of them would be aggravated if they navigated to a site and the toolbar or menu bar just disappeared. You should not pursue this idea. If you want a window without a toolbar or menu bar, you need to launch your own Internet Explorer window and not use an existing one. You can do this by closing the parent window after spawning the new window. It isn't exactly what you're asking for, but the appearance will be the same. Some programmers launch Internet Explorer without toolbars because they have an app that doesn't like the back button or won't work properly around the limitations of their apps, but that's not the right approach.

A You'll probably find that some users have opened Internet Explorer and set the options to show the toolbar and menu bar. Many of them would be aggravated if they navigated to a site and the toolbar or menu bar just disappeared. You should not pursue this idea. If you want a window without a toolbar or menu bar, you need to launch your own Internet Explorer window and not use an existing one. You can do this by closing the parent window after spawning the new window. It isn't exactly what you're asking for, but the appearance will be the same. Some programmers launch Internet Explorer without toolbars because they have an app that doesn't like the back button or won't work properly around the limitations of their apps, but that's not the right approach.

Q I want to prevent the following message being displayed: "This page contains a script which is taking an unusually long time to finish. To end this script now, click Cancel." This dialog is displayed when long scripts, up to 30 minutes or more, are running. The user must click OK again and again.

Q I want to prevent the following message being displayed: "This page contains a script which is taking an unusually long time to finish. To end this script now, click Cancel." This dialog is displayed when long scripts, up to 30 minutes or more, are running. The user must click OK again and again.

A What kind of script takes so long to complete? If your script ran outside of Internet Explorer, this dialog will not be shown to the user at all. That dialog is activated to prevent denial of service attacks from malicious scripts running in Internet Explorer (or just plain buggy ones with mistakes like infinite loops).

A What kind of script takes so long to complete? If your script ran outside of Internet Explorer, this dialog will not be shown to the user at all. That dialog is activated to prevent denial of service attacks from malicious scripts running in Internet Explorer (or just plain buggy ones with mistakes like infinite loops).

This dialog is not timed, but rather one that is displayed after 5,000,000 script statements are executed in the current page. The user can increase the default number of statements that trigger the dialog on his machine. To see how, read about MaxScriptStatements in Knowledge Base article 175500 ("Set Timeout Period for Script") for more complete information. It provides the registry key you need for changing the default number of script statements executed before this dialog is presented to the user.

Q I need to know the ramifications of disabling the sessionState element in machine.config. What sort of applications and scenarios are affected by this setting? I want to enable the Web garden feature in IIS 6.0, but I heard that in-process session state doesn't work very well.

Q I need to know the ramifications of disabling the sessionState element in machine.config. What sort of applications and scenarios are affected by this setting? I want to enable the Web garden feature in IIS 6.0, but I heard that in-process session state doesn't work very well.

A If you set sessionState to Off in machine.config, the only effect it will have is to break any ASP.NET application that is using session state. If you don't have any apps using session state, that's fine.

A If you set sessionState to Off in machine.config, the only effect it will have is to break any ASP.NET application that is using session state. If you don't have any apps using session state, that's fine.

Session state is used by ASP.NET applications to store user state, so if, for example, you have a three-page wizard that asks you for your date of birth, address, and job details, the first two pages might store the answers in session state, and the third page might use all the values to create a record in the database (there are better ways of doing this, but you get the idea).

Why do you want to enable Web gardens? Some MSDN® documentation (Performance Application Pool Settings) recommends against enabling Web gardens until you've done performance profiling that proves that it speeds things up. Do you know for certain that this will make things faster? It may actually make things slower. You have to consider your particular scenario. Do you use a lot of application state? Do you have lots of CPUs or lots of memory?

It's true that in-process session state will not work with an application that's spread across multiple worker processes, as in a Web garden, because the multiple processes need to share the state, and state is held in memory in one of the processes. You'll need to redesign your Web app so it doesn't use session state or turn to using the ASP.NET state server or SQL session state (which is slower)—there is no magic tweak. For more on Web gardens, see Configuring Web Gardens and IIS Insider - August 2003.

Q Are there any design guidelines or best practices around status popups that occur while an app is thinking or waiting for some external process to respond? In such a dialog, should there always be a close button to dismiss it? Is it bad to grab focus if the app is buried? If you navigate away (click elsewhere), do you necessarily need a way to come back through the taskbar or Alt-Tab?

Q Are there any design guidelines or best practices around status popups that occur while an app is thinking or waiting for some external process to respond? In such a dialog, should there always be a close button to dismiss it? Is it bad to grab focus if the app is buried? If you navigate away (click elsewhere), do you necessarily need a way to come back through the taskbar or Alt-Tab?

A In status dialogs, Cancel can be ambiguous. Close is a windowing command; clicking it closes the window and doesn't imply anything else about any pending or ongoing operations. Cancel is often used as a windowing command, but it usually means to discard any changes in addition to closing the window. In the case of a status dialog, there are no changes to save or discard. So, does Cancel mean to close the window, stop the operation, or both? It isn't clear, which is why you should put it in the helper text—something like "Click Cancel to stop the operation."

A In status dialogs, Cancel can be ambiguous. Close is a windowing command; clicking it closes the window and doesn't imply anything else about any pending or ongoing operations. Cancel is often used as a windowing command, but it usually means to discard any changes in addition to closing the window. In the case of a status dialog, there are no changes to save or discard. So, does Cancel mean to close the window, stop the operation, or both? It isn't clear, which is why you should put it in the helper text—something like "Click Cancel to stop the operation."

Another approach is to have separate Stop and Close buttons. Having to click two buttons is annoying unless there is a reason to keep the status dialog up after the operation has been stopped. (Some reasons to keep the status dialog up are that the user can restart after stopping or the user might want to see what was done before it was stopped.)

Generally, it is bad style to grab the focus away from the app that had it. Users don't like it, especially when they're typing, because part of what they typed goes to the new window. (If they happened to type a space, and the focus in your status dialog is on the Cancel button, guess what just happened?) It's a problem when the user is typing into one application and a window pops up, grabs focus, steals the keyboard input, and disappears without the user knowing what happened. If your window responds to any keyboard input (even just escape to hide it without canceling), then stealing focus is decidedly unfriendly. Bear in mind that in the worst case, your window steals the input before it finishes rendering so the user has no idea what window just appeared or what happened. If you've ever accidentally dismissed an appointment in Outlook this way, then you'll understand. If your app is not the top one, who cares what the status is? And if it doesn't include important status info and a cancel button, why would you have it at all? The only time you should grab the focus is if the status is relevant to the current activity, that is, it affects the entire system. But this should be very, very rare.

Q I have two ADO.NET questions. First, I built a DataSet of n rows (rows[0] to rows[n-1]), then use SqlDataAdapter.Update(DataSet, string) to insert these rows into a table. Can it be guaranteed that the sequence of rows inserted into the table will always be the same as they were added to the DataSet, that is, the rows in the table will always be rows[0] to rows[n-1]?

Q I have two ADO.NET questions. First, I built a DataSet of n rows (rows[0] to rows[n-1]), then use SqlDataAdapter.Update(DataSet, string) to insert these rows into a table. Can it be guaranteed that the sequence of rows inserted into the table will always be the same as they were added to the DataSet, that is, the rows in the table will always be rows[0] to rows[n-1]?

The second question is, if the OnRowUpdated event handler is provided to handle insertion failure and not rethrow it, in case of a failure, is it true that Update will not throw an exception? And, will execution of subsequent rows not be suspended as it would when there is no event delegate?

A You should not rely on an artifact that may change in the future. If you need consistent order, then you may need to call the Update method on each individual row—though you would have to use a 1-element array, since the Update method doesn't accept individual rows.

A You should not rely on an artifact that may change in the future. If you need consistent order, then you may need to call the Update method on each individual row—though you would have to use a 1-element array, since the Update method doesn't accept individual rows.

Regarding the second question, you do not need an event handler to prevent the Update method from throwing exceptions. Just set the DataAdapter.ContinueUpdateOnError property to True. Each row that has a problem will be flagged.

Got a question? Send questions and comments to  webqa@microsoft.com.

Thanks to the following Microsoft developers for their technical expertise: Todd Carter, Jim Cheshire, Norman Drews, Shawn Elliott, Phillip Garding, Attila Hajdrik, Polita Huff, Demetrios Kalligerakis, Bruce Leban, Bin Li, Chris Lovett, Chris Manderson, Nathan Manis, James Owusu, Tim Pearson, Rob Relyea, Malcolm Stewart, Jeremiah S. Talkar, Paul Tallett, Garry Tan, Jeffrey Tan, Stephen Toub, Abdallah Toutoungi, and Garth Wolfendale.