Click to Rate and Give Feedback
Related Articles
Here the author introduces SQL Server Data Services, which exposes its functionality over standard Web service interfaces.

By David Robinson (July 2008)
Here the author answers questions regarding the Entity Framework and provides an understanding of how and why it was developed.

By Elisa Flasko (July 2008)
Here we present techniques for programmatic and declarative data binding and display with Windows Presentation Foundation.

By Josh Smith (July 2008)
Systems that handle failure without losing data are elusive. Learn how to achieve systems that are both scalable and robust.

By Udi Dahan (July 2008)
More ...
Articles by this Author
As we'll show, with just a few lines of JavaScript you can build a general-purpose framework for incorporating page turns into Silverlight 1.0 apps.

By Jeff Prosise (May 2008)
: Jeff Prosise presents great tips for Silverlight development, which while it's gaining wide adoption, still needs more documentation and best practices so developers can make the most of the dazzling new features.

By Jeff Prosise (Launch 2008)
Jeff Prosise shows how you can implement drag-and-drop functionality in your Web app with ASP.NET AJAX.

By Jeff Prosise (January 2008)
Jeff Prosise explains when it's better to use UpdatePanel and when it's better to use asynchronous calls to WebMethods or page methods instead.

By Jeff Prosise (June 2007)


By Jeff Prosise (March 2007)


By Jeff Prosise (July 2006)
Data-driven site navigation is among the niftiest and most useful features in ASP. NET 2. 0. To get it working, all you do is create an XML site map file (or a SQL site map if you're using the MSDN®Magazine SqlSiteMapProvider), add a SiteMapDataSource, and bind a TreeView or Menu to the SiteMapDataSource.

By Jeff Prosise (June 2006)
Now that ASP.NET 2.0 is a shipping product, it seems appropriate to revisit an issue that tops the new features wish lists of many developers: a SQL Server™ site map provider.

By Jeff Prosise (February 2006)
More ...
Popular Articles
Here we introduce you to some of the concepts behind the new F# language, which combines elements of functional and object-oriented .NET languages. We then help you get started writing some simple programs.

By Ted Neward (Launch 2008)
Learn how to create a workflow that uses InfoPath forms and other office documents for passing data to targeted activities and for use in Office documents.

By Rick Spiewak (June 2008)
The .NET Compact Framework 3.5 provides a subset of Windows Communication Foundation (WCF) functionality that you can harness to communicate between Windows Mobile devices and desktop PCs. We'll show you how.

By Andrew Arnott (Launch 2008)
Jamie Laflen extols the benefits of TDD when applied to database development—and supplies some useful techniques along the way.

By Jamie Laflen (Launch 2008)
More ...
Read the Blog
SQL Server Data Services (SSDS) is a robust, scale-free data service that internally uses proven SQL Server technology and exposes its functionality over industry standard Web service interfaces. In the July 2008 issue of MSDN Magazine, David Robinson introduces ...
Read more!
Windows Presentation Foundation (WPF) offers excellent support for managing the display and editing of complex data. In the December 2007 edition of MSDN Magazine, John Papa did a great job of explaining essential WPF data binding concepts. ...
Read more!
The most fundamental form of Web testing is HTTP request/response testing. This involves programmatically sending an HTTP request to the Web application, fetching the HTTP response, and examining the response for an expected value. In the May 2008 issue of MSDN Magazine, Read more!
In the November issue of MSDN Magazine, Jeffrey Richter demonstrates some recent additions to the C# programming language that make working with the APM significantly easier. In the June ...
Read more!
The July 2008 issue of MSDN Magazine is now available online. Here's what's in the issue: Data Services: Develop ...
Read more!
The June 2008 issue features the first installment of a new MSDN Magazine column on software design fundamentals. We’ll discuss design patterns and principles in a manner that isn't bound to a specific tool or lifecycle methodology. In this issue, Jeremy Miller starts the Patterns in Practice column ...
Read more!
More ...
Wicked Code
Running ASMX Web Services on STA Threads
Jeff Prosise

Code download available at: WickedCode2006_10.exe (152 KB)
Browse the Code Online
Many of the gnarliest issues you read about in Wicked Code come from real problems experienced by real people trying to build software that works. Recently, I came across a problem that I had never encountered before, but that I'm certain other developers have or someday will experience. The problem was one of concurrency, and had to do with ASMX Web services and legacy COM components. The development team who brought the issue to my attention had built an ASMX Web service that relied on legacy COM components written in Visual Basic® 6.0 to perform key processing tasks.
The design called for a high degree of parallelism in processing requests, so each request submitted to the Web service created its own Visual Basic component instance, and then called it. Performance was abysmal. Investigation proved that despite the number of Web service threads making concurrent calls to an equal number of Visual Basic component instances (even if there were 20 or more), only one component instance was executing at a time.
In effect, calls were being queued up at the doorway to COM and serialized into the respective component instances. Because some of the calls required 20 seconds or more to complete, the problem wasn't one the dev team could ignore. Instead, it was a showstopper—the product could not ship until the problem was rectified.
After countless hours of trying to get a handle on the problem, the dev team realized they were up against a wall. Something was causing all those concurrent calls to be executed sequentially rather than in parallel. But what? Was it COM? Was it Visual Basic 6.0? Or was it a nuance of the ASMX architecture? Whatever the problem was, a solution had to be found fast. Otherwise, the schedule would slip and the entire project would be placed in jeopardy.

The Problem
In a perfect world, everyone writing code for Microsoft platforms would write managed code and would never again have to deal with legacy technologies, such as COM. But the reality is that millions of lines of critical business logic are encapsulated in COM components—Visual Basic 6.0 COM components, in particular—and ASP.NET developers frequently have no choice but to call those components from managed code.
The Microsoft® .NET Framework does a fine job of allowing managed code to call out to unmanaged COM components. For example, the Framework's Tlbimp.exe utility can import COM type libraries and generate Runtime Callable Wrappers (RCWs) that permit unmanaged components to be called as if they were managed. But neither Tlbimp.exe, nor any other .NET tool, can mitigate the concurrency issues that arise when calls go out from managed code to unmanaged COM components.
It was a concurrency issue like this that produced the problem the dev team had encountered. Figure 1 shows the threading configuration of the Web service and the components that it has created. In this example, the Web service is processing five concurrent requests. ASMX Web services use the same HTTP pipeline that ASPX pages use, so each request has been allocated a thread by ASP.NET. ASP.NET threads are, in reality, COM multithreaded apartment (MTA) threads—that is, they're threads that run in a COM MTA. Because Visual Basic 6.0 COM components are incompatible with COM MTAs, the component instances that are created by the Web service threads are not in the MTA with their creator threads. Instead, they live in a single-threaded apartment, or STA, created by COM. The STA, like all COM STAs, is driven by a single thread. And because all five component instances share an STA, they share that single thread.
Figure 1 Default Apartment Configuration 
The fact that all five component instances run on the same thread in the same apartment explains why calls are serialized. When one of the MTA threads calls a COM component, COM marshals the call from one apartment to the other, performing a thread switch in the process. If the STA thread is busy processing a call from one MTA thread, COM continues to queue calls from other MTA threads until the STA thread becomes available. All parallelism is lost when the call crosses the boundary from the MTA to the STA because the STA can only do one thing at a time. In effect, the STA is like a big mutual exclusion lock that causes caller B to wait until the call from caller A has completed.
You can prove that Web services run on MTA threads with the simple Web service, TestService.cs, shown in Figure 2. Its one and only Web method, Test, returns a string that indicates what type of COM apartment it's running in. Figure 3 shows what happens when you invoke the Test method from ASP.NET's autogenerated test harness. The string "MTA" in the results clearly shows that the request was processed by an MTA thread.
Figure 3 Proof of MTA Threads (Click the image for a larger view)
The scenario depicted in Figure 1 may seem contrived, but in fact it's extraordinarily common. Because ASP.NET requests run on MTA threads by default, callouts to STA-based COM objects (this includes all Visual Basic 6.0 COM components) are marshaled from the ASP.NET MTA into the COM STA. Developers often don't realize that the STA is a choke point and that seemingly concurrent calls to individual component instances are actually serialized by COM. This fact only becomes obvious when individual calls take a long time to complete.
This would be less of a problem if COM would create multiple STAs—one per MTA thread that creates an object instance. But COM doesn't do that, in part because COM is loath to spin up new threads in a process. (COM+ will do that, but COM+ is a different entity from COM.) When an MTA thread creates an STA-based object, COM is forced to create a new thread to drive that STA. When a second MTA thread creates an STA-based object, COM places that object instance in the STA it created for the first object instance. The same goes for the third object instance, fourth object instance, and so on.
Fortunately, there is a convenient solution to the COM STA bottleneck that page developers can use. While including an AspCompat="true" attribute in an ASPX's Page directive provides COM components access to ASP-style infrastructure such as Request and Response objects, it also has the very desirable effect of creating an STA thread pool and processing requests for that page with STA threads. Besides eliminating the marshaling overhead incurred by MTA-to-STA thread switches, AspCompat="true" allows STA COM objects (specifically, instances of COM classes registered ThreadingModel="Apartment") to be created in their creators' apartments provided the creators are running in STAs themselves. If there are five request processing threads running in STAs and each creates an STA COM object, then all five object instances share an apartment—and a thread—with their creator. COM is happy to place each object instance in a separate STA because it doesn't have to create the STAs (or threads to drive them). Calls to the objects are no longer queued to a single thread, and the objects can execute code concurrently.
Alas, there is no equivalent of AspCompat="true" for ASMX files. The WebService directive won't accept an AspCompat attribute. It's left to the ASMX developer to come up with a way to put ASMX requests on STA threads to avoid the STA bottleneck. Now let's look at the means for doing this.

The Solution
Before coding, I scanned a number of forums and blogs. I found a post at www.epocalipse.com/blog/category/general that proposed a solution that was both elegant and concise. It outlined an idea to write an HTTP handler that derives from System.Web.UI.Page, register it as the handler for ASMX files, and use the AspCompat infrastructure already in the Page class to lend AspCompat support to ASMX. The post was even accompanied by sample code (which was correct in principle, albeit flawed in implementation).
I built on this to produce the Page derivative shown in Figure 4. When it invokes the handler, ASP.NET calls AspCompatWebServiceHandler's BeginProcessRequest method, since AspCompatWebServiceHandler implements the IHttpAsyncHandler interface. (Without that interface, ASP.NET would call the handler's synchronous ProcessRequest method instead.) BeginProcessRequest delegates to the AspCompatBeginProcessRequest method inherited from Page. This method serves as the gateway to the AspCompat infrastructure that is built into the Page class. Shortly after AspCompat processing begins, AspCompatWebServiceHandler's OnInit method is called. This creates a normal ASMX HTTP handler (new WebServiceHandlerFactory.GetHandler) and calls its ProcessRequest method, passing in the HTTP context for the current request. Thus, the request undergoes the normal ASMX processing, but it does so within an AspCompat wrapper that processes the request with an STA thread instead of an MTA thread.
In order for AspCompatWebServiceHandler to work its magic, it must be registered as the HTTP handler for ASMX files. You can register it using this web.config file:
<configuration>
  <system.web>
    <httpHandlers>
      <add verb="*" path="*.asmx"
        type="AspCompatWebServiceHandler, __code" />
    </httpHandlers>
  </system.web>
</configuration>
Note the assembly name used in the type attribute: __code. This assumes that you have placed the source code for AspCompatWebServiceHandler in the application's App_Code directory and allowed ASP.NET to generate an assembly from it. If, instead, you compiled the assembly yourself and place it in the bin directory, simply replace __code with the assembly name. (Since ASP.NET 1.x doesn't support App_Code directories, you'll have to do this if you're not running ASP.NET 2.0.)
In order to confirm that ASMX requests are now processed with STA threads, run TestService again after installing AspCompatWebServiceHandler. Figure 5 shows the results. The thread that ASP.NET assigned to the request is clearly an STA thread. Mission accomplished!
Figure 5 Confirming Use of STA Threads (Click the image for a larger view)
While it is a bit of a hack (there's often a very fine line between a hack and wicked code!), this solution worked flawlessly on the problem application. Figure 6 shows the threading and apartment configuration of the modified app. The five request processing threads are now STA threads living in separate STAs. Because COM places STA object instances in the STAs of their creators when the creators are STA threads, each object instance now has its own STA. Moreover, calls to the objects now execute in parallel because each object instance runs on a separate thread.
Figure 6 New Apartment Configuration 

Conclusion
Besides eliminating MTA-to-STA marshaling overhead, including the AspCompat="true" attribute allows COM to place object instances in separate STAs rather than lumping them into one apartment (and running them on one thread). Although AspCompat isn't supported for Web services, you can add it to your ASMX code and enjoy the same efficiency in calling Visual Basic 6.0 COM components from Web services as you do from Web pages.
If you'd like to learn more about COM threading and apartments, check out a pair of articles I wrote back when COM was cool. The first is available at www.codeguru.com/cpp/com-tech/activex/apts/article.php/c5529 and the second at www.codeguru.com/cpp/com-tech/activex/apts/article.php/c5533. The articles are several years old, but the content is just as pertinent today as it was back then—especially if you're a programmer who's into .NET and faced with the challenge of calling old COM components from managed code.

Send your questions and comments for Jeff to  wicked@microsoft.com.


Jeff Prosise is a contributing editor to MSDN Magazine and the author of several books, including Programming Microsoft .NET (Microsoft Press, 2002). He is also a cofounder of Wintellect, a software consulting and education firm.

Page view tracker