Practical ASP.NET

Determine Performance Requirements

Take advantage of the Windows Management Interface to correctly gauge your application's performance requirements.

Technology Toolbox: C#, ASP.NET, ASP.NET 2.0

Developing an enterprise application entails making countless decisions about its design and implementation before it ever enters the production environment in which it will operate. For example, you might wonder whether your application will maintain the required level of service when you deploy it. Or, you might have concerns about its greatest load times or the effects on transactional throughput under stress. How do you know the model you're using to estimate performance will reflect the reality after deployment?

You can use many different techniques to assist you in determining your application's performance requirements, but your greatest chance for success comes from a balanced strategy. It's okay to trust your projections as you create your application, but you also need to verify that they conform to your projections once you deploy the application.

One tool that can help you is the Windows Management Instrumentation (WMI). WMI is an object-oriented system management API based on a standard Common Information Model (CIM) schema supported by several vendors. WMI predates .NET as a large set of scriptable COM objects, highly popular with savvy system developers administering Windows' myriad knobs and dials. It enables applications to manage systems, monitor applications and IT assets, and present themselves for management and monitoring through instrumentation, which is precisely the sort of lens you can use to take a revealing look at your applications after they've been released into their natural habitat.

I'll show you how to connect your applications to preexisting WMI providers using familiar databinding practices. Accessing the process and thread objects will enable you to measure your application's processing time, memory management, working set size, and even potentially thread deadlocking situations (see the WMI documentation in the MSDN Library for information on ThreadWaitReason's values). At first glance, using the .NET Framework for this kind of monitoring looks like a difficult task, but you'll see that the System.Management namespace makes incorporating this kind of monitoring functionality a cinch.

Before diving into the sample, let's take a good look at what WMI is and how you can put it to use. In WMI, classes representing manageable hardware or software entities are organized into hierarchical namespaces. Namespaces act as categories that group related classes together and scope names to avoid naming collisions, ensuring that classes in separate namespaces with the same class name don't conflict. Classes based on the standard CIM schema have well-known namespaces, while vendor-specific WMI objects generally belong to namespaces based on the company and product names.

Classes can have one or more instances. For example, you can partition an application server's disk so it can host multiple applications. Partitions comprise a class with common characteristics, but each individual partition is a separate instance. Instances can expose properties that WMI clients can query, methods that WMI clients can invoke to manage the instance, and events that WMI clients subscribe to in order to receive notifications when something of interest happens.

Many developers are overwhelmed by the number of WMI classes initially, but the .NET Framework's simple interface lets you use only what you need. Using them is made easier by the fact that the object orientation of the WMI model parallels concepts from the Common Language Runtime (CLR) you might be familiar with already (see Table 1). Visual Studio .NET 2005 can also help you navigate WMI classes in the Management extension of the Server Explorer.

WMI support in the .NET Framework exists under the System.Management namespace. It contains the classes you'll need to act as a client of WMI information. You use the classes in the System.Management.Instrumentation namespace to serve WMI information to other clients. ASP.NET 2.0 adds a third namespace named System.Web.Management that is tailored specifically for Web applications. The examples in this article confine themselves to the System.Management namespace, illustrating essentials that aren't Web-specific.

Specifically, I'll walk you through binding to WMI using Visual Studio .NET 2005, XML, and ADO.NET to create a Web-based view of the ASP.NET worker process's threads. The Win32_Process and Win32_Thread classes offer a lot of useful information for monitoring the performance of an application, so it makes a good starting point for considering your own application's timing and memory-monitoring needs. I'll use an ASP.NET example, but WMI is equally useful for rich client applications and services. ASP.NET services Web requests, so it already provides substantial built-in instrumentation that you can surface easily.

Drill Down on WMI Classes
Begin your exploration of the built-in WMI classes by opening the Server Explorer (Ctrl-Alt-S). This application is an explorer-style interface within Visual Studio .NET. It provides developers with a convenient tool for browsing through resources, including data schemas, performance counters, and yes, WMI events and classes. You can drag many objects you view in the Server Explorer onto the IDE design surface and generate code automatically.

Next, navigate through the treeview in this window under Servers and your machine name. (If you are on a network, you might want to add other machines under Servers for browsing). Expand the node labeled Management Classes to see a list of high-level WMI classes that you can manage (see Figure 1).

Many of WMI's classes are nested in hierarchical relationships. For example, Win32_Threads are children of Win32_Process. The process table on a developer's workstation can prove highly volatile, so you might need to click on the Refresh button on the Server Explorer toolbar occasionally to bring these contents up to date.

In this example, you want to create a solution built around a new Web site in Visual Studio .NET 2005 (File | New | Web Site?) named WmiGridView. Using the WMI support in .NET requires that your Web site add an assembly reference to the System.Management.dll assembly. Right-click on the Web site in the Solution Explorer and select Add References? to add this assembly into your project. Then select Add New Item to include an empty XSLT stylesheet named "wmi2ds.xslt" to the Web site.

WmiGridView achieves its databinding at a high level by walking through a compact series of steps (see Figure 2). It extracts WMI instances from the WMI Repository provided by the winmgmt service that runs in the background. Next, WmiGridView takes the XML representation of these instances and transforms them with XSLT into the XML representation of an ADO.NET data set. The transformation maps Classes to DataTables, Instances to DataRows, and Properties to DataColumns. Finally, the application binds the GridView to the extracted and transformed information contained in the ADO.NET DataSet (see Figure 3).

Before delving into the intricacies of mapping between XML formats, you must confront the issue of enumerating threads. WMI Query Language (WQL) offers constructs that you'll recognize immediately if you've spent any time in relational databases using SQL. For example, consider this query:

select * from Win32_Threads
where ProcessHandle = (
   select ProcessId from Win32_Process
   where Name = "aspnet_wp.exe"
)

This selection applies equally well to a relational database modeling an operating system and WMI, and it's typical of the System.Management code you'll be writing for WmiGridView. You create a SelectQuery object in .NET to represent each part of this selection, and a ManagementObjectSearcher acts like a data reader that enables you to enumerate through the resulting ManagementObjects. You read WMI properties much as you look up values in a dictionary, where the keys are the property names:

SelectQuery subquery =
   new SelectQuery("Win32_Thread",
   string.Format("ProcessHandle=\"{0}\"",
   procInstance["ProcessId"]));

searcher = new ManagementObjectSearcher(subquery);

ManagementObjectCollection threads = searcher.Get();
   foreach (ManagementObject threadInstance in
      threads) { . . . }

EnumerateWin32Threads does more than execute queries; it also processes each Win32_Thread instance by appending its XML into an XmlDocument container. Typically, you cannot append XML fragments to the root of the XmlDocument directly because doing so more than once violates a document's requirement of having only one root element. Creating a single document element node in the XmlDocument container now allows adding multiple fragments as child nodes, where each fragment represents a thread instance and is well-balanced.

The XML is retrieved as a string using ManagementObject's GetText method using a TextFormat of CimDtd20. CimDtd20 and WmiDtd20 are each minor schema variations on the other, and it's a moot choice for WmiGridView. The XmlReader converts an XML string to an XmlDocument. Before you can append the new node tree to the primary document, you must call ImportNode to alter its owner document.

Note that the sample creates the XmlReader with .NET 2.0's new creational pattern. In this case, you prepare the XmlReaderSettings as a kind of profile for the reader implementation. You take this profile and pass it to the Create factory method, which is supposed to hand back an XmlReader implementation optimized for this profile.

Examining the natural XML representation of the thread instances can help you understand the mapping wmi2ds.xslt will do (see Listing 1). Copy EnumerateWin32Threads into default.aspx.cs, and call it from the Page_Load event handler (if you have no Page_Load event handler, you can switch to design view and double-click anywhere on the page to add one). Before calling EnumerateWin32Threads, remember to initialize the XmlDocument and add a single document element to it. Next, drag an ASP.NET Label onto the design view and then add this line after the call to EnumerateWin32Threads:

Label1.Text = doc.OuterXml.Replace( 
   "<","<").Replace(">","><br>");

This code displays the original XML representation in your browser when you run it with a quick and dirty XML viewer that works by escaping the < character. While well structured, this approach isn't quite suitable for displaying tabular data in a WmiGridView at this point in the sample. WmiGridView needs more information; the missing data includes both simple element content and attribute values. You could use the ManagementObjects enumerated here to poke the needed information into DataTables, but XSLT can eliminate this manual drudgery.

Transform the DataSet
XSLT is a standard XML-related technology especially effective for mapping one XML source document structure to another XML result document structure. For example, examine the wmi2ds.xslt stylesheet (see Listing 2). Most stylesheets consist of two types of XML elements. Elements controlling how the transformation is performed reside in the "http://www.w3.org/1999/XSL/Transform" namespace URI, and conventionally have the prefix "xsl." All other elements are literal XML that's transferred verbatim into the resulting XML document. As in procedure-oriented programs, the stylesheet is divided into simple templates that get applied successively, starting with the root template—which matches the root (/) of the source XML document coming from WMI. Each template emits XML into the resulting XML document and applies further templates until the transformation is complete. Note that the expression in the select attribute matches the expressions in match attributes of other templates.

This stylesheet has a few more characteristics worth taking a closer look at. First, note how it uses the xsl:element tag. Each DataRow in the resulting document must have the same name as its DataTable. For WMI instances, this name comes from the CLASSNAME attribute of the source document. You must use xsl:element to specify the tag name before you can use the CLASSNAME attribute value as the name of an XML element in the resulting document. Using the name attribute on xsl:element allows you to specify an interpolative XPath expression (because of the curly braces in the name attribute value) that looks up the corresponding CLASSNAME attribute from the source document and uses its value.

Pay attention as well to how the stylesheet attempts to cope with WMI's dual-edged sword: WMI gives you plenty of information, but it can give you too much information. The CIM schema associates numerous metaproperties on its classes, and those are in the XML source document as well. Their NAME attributes start with two underscores, but they add little value to the report you want to see. The sample uses an xsl:choose function here (much as you would use a switch or Select Case statement in other languages), showing off some of XPath's built-in functions to exclude emitting their XML into the resulting document.

The .NET Framework's XslTransform (and.NET 2.0's XslCompiledTransform) allow you to run XSLT stylesheets to transform input XML to the desired output XML. I chose XslTransform for this example because it has overloads returning an XmlReader that allow for straight-through processing of the transformed XML as it gets loaded into the DataSet. In the beta 2 release, XslCompiledTransform offers only overloads returning void, so it doesn't permit this optimization (I would need to have an intermediate deserialize/serialize step instead), but I'm hopeful this will change in a future release.

The sample includes additional logic that loads the stylesheet and performs the transform (see Listing 3). The ReadXml method call on DataSet accepts an XmlReader, which is precisely what XslTransform returns, so the construction of the DataSet can coincide with the result document that gets formed node by node. After dragging a GridView and auto-formatting it, the last two lines of code databind it to the DataSet. DataSource is set to the DataSet now populated with WMI thread information, and DataBind is called.

Connecting your applications to process and thread objects by databinding to WMI provider information is only the first step. Next, you should explore the other kinds of information WMI provides for building monitoring and management capabilities into your applications. The MSDN Library provides detailed documentation on the properties of classes that you can bind to GridView and FormView controls using the techniques described in this article. When your needs exceed the standard CIM classes, try publishing your own properties and methods. The .NET Framework's managed support for WMI makes it easy to take advantage of WMI's features in your applications.

comments powered by Disqus
Upcoming Events

.NET Insight

Sign up for our newsletter.

I agree to this site's Privacy Policy.