 MSDN Magazine August 2005

MSDN Magazine August 2005

Concurrency: What Every Dev Must Know About Multithreaded Apps

Multithreading offers lots of benefits, but it's not a technique you can employ willy nilly without first learning about all the potential pitfalls. To get you started, Vance Morrison covers multithreading and the shared memory threading model, race conditions and how concurrent access can break invariants. He also discusses how to use locks so you can understand the costs involved.

Vance Morrison Proxy Detection: Take the Burden Off Users with Automatic Configuration in .NET

Proxy settings can be a real pain in the neck and very frustrating for users to configure properly. In this article, Durgaprasad Gorti discusses how you can use automatic proxy configuration via script and how it works in the Microsoft .NET Framework 1.1 and 2.0.

Durgaprasad Gorti Data Binding: Give Your Everyday Custom Collections a Design-Time Makeover

In this article, Paul Ballard shows how you can provide data binding support for your custom collections to enable sorting, searching, and editing in as simple a manner as possible. In addition, he shows how to make all of these features available in the Windows and Web Forms Designers, just like an ADO.NET DataSet.

Paul Ballard Winsock: Get Closer to the Wire with High-Performance Sockets in .NET

The Win32 Windows Sockets library (Winsock) provides mechanisms to improve the performance of programs that use sockets, and the Microsoft .NET Framework provides a layer over Winsock so that managed applications can communicate over sockets. To use all these layers to write a truly high-performance socket-based application requires a little background information, as Daryn Kiely explains here.

Daryn Kiely Easy UI Testing: Isolate Your UI Code Before It Invades Your Business Layer

The User Interface Process Application Block (UIP) from the Microsoft Patterns & Practices team can help you isolate your UI, write unit tests for your UI logic, and write a UI that really is the thin layer it was always meant to be. Mark Seemann shows you how it's done in this article.

Mark Seemann Editor's Note: Connected Systems Developer Competition

Last month, we told you about a contest to help design the next-generation PC hardware and we know you're working feverishly on your entries for that. Well, this month we have news of an even more exciting competition—one that MSDN Magazine is proudly co-sponsoring.

New Stuff: Resources for Your Developer Toolbox

Tired of spending oodles of time trying to reproduce bugs so you can then fix them? RadViewSoftware Inc. has a new addition to their Web application function and load testing tools that captures everything that goes on at the client and the server in synchronized logs.

Marnie Hutcheson Extreme ASP.NET: Tools of the Trade: SQL Server Profiler and Query Analyzer

In my last column, I discussed Microsoft® Application Center Test and how it could be used to measure the performance of your Web application (see Extreme ASP. NET: Tools of the Trade: Application Center Test).

Rob Howard Basic Instincts: Reflection in Visual Basic .NET

In the May 2005 Basic Instincts column I wrote about the Microsoft® . NET Framework support for custom attributes and attribute-based programming (see Basic Instincts: Designing With Custom Attributes).

Ted Pattison Data Points: The Enterprise Library Data Access Application Block, Part 2

Last month I explored the foundation of the Enterprise Library Data Access Application Block (DAAB) including how it all fits into an architecture (see Data Points: The Enterprise Library Data Access Application Block, Part 1).

John Papa Cutting Edge: DataSets vs. Collections

In software, five years is like a geological era. Five years ago, the Microsoft® . NET Framework had just been announced. Since then, the DataSet has emerged as the key object for performing a variety of data-related tasks in .

Dino Esposito Test Run: Test Harness Design Patterns

The Microsoft® . NET Framework provides you with many ways to write software test automation. But in conversations with my colleagues I discovered that most engineers tend to use only one or two of the many fundamental test harness design patterns available to them.

James McCaffrey and James Newkirk Service Station: The Application Connection Designer

In some of my past columns on service orientation and contract-first development, I've mentioned the new suite of designers, formerly codenamed "Whitehorse," that are part of Visual Studio® Team edition for Software Architects Team System.

Aaron Skonnard Pure C++: Generic Programming: Template Specialization

As I discussed in my last column, a param-eterized type that does more than simple storage and retrieval is constrained as to the acceptable types that may be safely bound to it (see Pure C++: CLR Generics Versus C++ Templates.

Stanley B. Lippman C++ at Work: Dialog Templates, RegexTest

Paul DiLascia { End Bracket }: Improving Managed DirectX Performance

It seems that at least twice a week, I am asked about poor performance in Managed DirectX®. This frequency is actually a big improvement over the 5-10 times a week I was asked a few years ago when the technology first came out.

Tom Miller


Concurrency: What Every Dev Must Know About Multithreaded Apps

Concurrency

What Every Dev Must Know About Multithreaded Apps

Vance Morrison
This article discusses:
  • Multithreading and the shared memory threading model
  • Races and how concurrent access can break invariants
  • Locks as the the standard solution to races
  • When locks are needed
  • How to use locks and understand costs
  • How locks can get in each other's way
This article uses the following technologies:
.NET Framework
Contents
Threads and Memory
Races
Locks
Using Locks Properly
How Many Locks?
Taking Locks on Reads
What Memory Needs Lock Protection?
Methodical Locking
Deadlock
The Cost of Locks
A Quick Word on Synchronization
Conclusion

T
en years ago only hard-core systems programmers worried about the intricacies of writing correct code in the presence of more than one thread of execution. Most programmers stuck with sequential programs to avoid the problem altogether. Now, however, multiprocessor machines are becoming commonplace. Soon, programs that are not multithreaded will be at a disadvantage because they will not be using a large part of the available computing power.

Unfortunately, writing correct, multithreaded programs is not easy. Programmers are simply not accustomed to the idea that other threads might be changing memory out from underneath them. Worse, when a mistake is made, the program will continue to work most of the time. Only under stressful (production) conditions will the bugs manifest, and only rarely will there be enough information at the point of failure to effectively debug the application. Figure 1 summarizes the important differences between sequential and multithreaded programs. As it shows, it really pays to get multithreaded programs right the first time.

Figure 1 Characteristics of Sequential and Multithreaded Programs (Close Figure 1)

Sequential Programs | Multithreaded Programs
Can only utilize a single processor | Can take advantage of multiprocessor hardware
Behavior is usually deterministic | Behavior is usually nondeterministic
Testing that covers all code finds most bugs | Code coverage alone is insufficient; threads racing to access memory cause the most problematic bugs
Root causes for failures are found by carefully tracing execution that leads to failure | Root causes are found by postulating a race that fits the behavior and methodically reviewing the code for possible causes
Once identified, finding a fix for a bug is generally assured | The root cause for a race can easily remain unidentified

I have three goals in this article. First, I'll show that multithreaded programs are not that arcane. The fundamental requirement for writing correct programs is the same whether the program is sequential or multithreaded: all code in the program must protect any invariants that other parts of the program need. Second, I'll show you that while this principle is simple enough, preserving program invariants is much more difficult in the multithreaded case. Examples that are trivial in a sequential environment have surprising subtlety in a multithreaded environment. Finally, I'll also show you how to deal with the subtle problems that can creep into your multithreaded programs. This guidance amounts to strategies for being very methodical about protecting program invariants, which, as the table in Figure 2 shows, is more complicated in the multithreaded case. There are a number of reasons that this is more complicated when using multithreading and I will explain them in the following sections.

Figure 2 Programming in Sequential and Multithreaded Programs (Close Figure 2)

Sequential Programs | Multithreaded Programs
Memory is stable unless explicitly updated | Memory is in flux unless it is read-only, thread local, or protected by a lock
No locks are needed | Locks are essential
Data structure invariants need to hold on entry and exit of the data structure's methods | Data structure invariants have to hold any time the lock protecting the data structure is not held
No special considerations when accessing two interdependent data structures | If two data structures are related, locks for both structures must be entered before using that relationship
Deadlock can't happen | Deadlock is possible when there are multiple, unordered locks

Threads and Memory

At its heart, multithreaded programming seems simple enough. Instead of having just one processing unit doing work sequentially, you have two or more executing simultaneously. Because the processors might be real hardware or might be implemented by time-multiplexing a single processor, the term "thread" is used instead of processor. The tricky part of multithreaded programming is how threads communicate with one another.

Figure 3 Shared Memory Threading Model
Figure 3 Shared Memory Threading Model

The most commonly deployed multithreaded communication model is called the shared memory model. In this model all threads have access to the same pool of shared memory, as shown in Figure 3. This model has the advantage that multithreaded programs are programmed in much the same way as sequential programs. That advantage, however, is also its biggest problem. The model does not distinguish between memory that is being used strictly for thread local use (like most locally declared variables), and memory that is being used to communicate with other threads (like some global variables and heap memory). Since memory that is potentially shared needs to be treated much more carefully than memory that is local to a thread, it's very easy to make mistakes.

Races

Imagine a program that processes requests and has a global counter, totalRequests, that is incremented after every request is completed. As you can see, the code that does this for a sequential program is trivial:

totalRequests = totalRequests + 1

If, however, the program has multiple threads servicing requests and updating totalRequests, there is a problem. The compiler might compile the increment operation into the following machine code:

MOV EAX, [totalRequests]  // load memory for totalRequests into register
INC EAX                   // update register
MOV [totalRequests], EAX  // store updated value back to memory

Consider what would happen if two threads ran this code simultaneously. As shown in Figure 4, both threads will load the same value for totalRequests, both will increment it, and both will store it back to totalRequests. At the end of that sequence two threads have processed a request; however, the value in totalRequests will only be one larger than it was previously. Clearly, this isn't what you want. Bugs like this, which occur because of bad timing between threads, are called races.

Figure 4 Anatomy of a Race
Figure 4 Anatomy of a Race

While this example may seem trivial, the general structure for the problem is the same as for more complicated real-life races. There are four conditions needed for a race to be possible.

The first condition is that there are memory locations that are accessible from more than one thread. Typically, locations are global/static variables (as is the case of totalRequests) or are heap memory reachable from global/static variables.

The second condition is that there is a property associated with these shared memory locations that is needed for the program to function correctly. In this case, the property is that totalRequests accurately represents the total number of times any thread has executed any part of the increment statement. Typically, the property needs to hold true (that is, totalRequests must hold an accurate count) before an update occurs for the update to be correct.

The third condition is that the property does not hold during some part of the actual update. In this particular case, from the time totalRequests is fetched until the time it is stored, totalRequests does not satisfy the invariant.

The fourth and final condition that must occur for a race to happen is that another thread accesses the memory when the invariant is broken, thereby causing incorrect behavior.

Locks

The most common way of preventing races is to use locks to prevent other threads from accessing memory associated with an invariant while it is broken. This removes the fourth condition I mentioned, thus making a race impossible.

The most common kind of lock goes by many different names. It is sometimes called a monitor, a critical section, a mutex, or a binary semaphore, but regardless of the name, it provides the same basic functionality. The lock provides Enter and Exit methods, and once a thread calls Enter, all attempts by other threads to call Enter will cause the other threads to block (wait) until a call to Exit is made. The thread that called Enter is the owner of the lock, and it is considered a programming error if Exit is called by a thread that is not the owner of the lock. Locks provide a mechanism for ensuring that only one thread can execute a particular region of code at any given time.

In the Microsoft® .NET Framework locks are implemented by the System.Threading.Monitor class. The Monitor class is a bit unusual because it does not define instances. This is because lock functionality is effectively implemented by System.Object, and so any object can be a lock. Here is how to use a lock to fix the race associated with totalRequests:

static object totalRequestsLock = new Object();  // executed at program
                                                 // init
...
System.Threading.Monitor.Enter(totalRequestsLock);
totalRequests = totalRequests + 1;
System.Threading.Monitor.Exit(totalRequestsLock);

While this code does fix the race, it can introduce another problem. If an exception happens while the lock is held, then Exit will not be called. This will cause all other threads that try to run this code to block forever. In many programs, any exception would be considered fatal to the program and thus what happens in that case is not interesting. However, for programs that want to be able to recover from exceptions, the solution can be made more robust by putting the call to Exit in a finally clause:

System.Threading.Monitor.Enter(totalRequestsLock);
try {
    totalRequests = totalRequests + 1;
} finally {
    System.Threading.Monitor.Exit(totalRequestsLock);
}

This pattern is common enough that both C# and Visual Basic® .NET have a special statement construct to support it. The following C# code is equivalent to the try/finally statement just shown:

lock(totalRequestsLock) {
    totalRequests = totalRequests + 1;
}
Personally, I am ambivalent about the lock statement. On the one hand, it is convenient shorthand. However, it can lull programmers into a false sense of confidence that they are writing robust code. Remember, the locked region was introduced because an important program invariant did not hold there. If an exception is thrown in that region, it is likely that the invariant was broken at the time the exception was thrown. Allowing the program to continue without trying to fix the invariant is a bad idea.

In the totalRequests example, there is no useful cleanup to do so the lock statement is appropriate. The lock statement would also be appropriate if everything its body did was read-only. In general, however, more cleanup work needs to be done if an exception happens. In this case a lock statement does not add much value since an explicit try/finally statement will be needed anyway.

Using Locks Properly

Most programmers have had some interaction with races and have seen simple examples of how to use locks to prevent them. Without additional explanation, however, the examples themselves don't drive home important principles needed to use locks effectively in real-world programs.

The first important observation is that locks provide mutual exclusion for regions of code, but generally programmers want to protect regions of memory. In the totalRequests example, the goal is to make certain an invariant holds true on totalRequests (a memory location). However, to do so, you actually place a lock around a region of code (the increment of totalRequests). This provides mutual exclusion over totalRequests because it is the only code that references totalRequests. If there was other code that updates totalRequests without entering the lock, then you would not have mutual exclusion for the memory, and consequently the code would have race conditions.

This leads to the following principle. For a lock to provide mutual exclusion for a region of memory, no writes to that memory can occur without entering the same lock. In a properly designed program, associated with every lock is a region of memory for which it provides mutual exclusion. Unfortunately, there is no obvious artifact of the code that makes this association clear, and yet this information is absolutely critical for anyone reasoning about the multithreaded behavior of the program.

As a consequence of this, every lock should have a specification associated with it that documents the precise region of memory (set of data structures) for which it provides mutual exclusion. In the totalRequests example, totalRequestsLock protects the totalRequests variable and nothing else. In real programs, locks tend to protect larger regions, such as a whole data structure, several related data structures, or all memory reachable from a data structure. Sometimes a lock only protects part of a data structure (like the hash bucket chain of a hash table), but whatever the region is, it is critical that the programmer document it. With the specification written down, it is possible to methodically validate that the lock is entered before the associated memory is updated. Most races are caused by the failure to consistently enter the correct lock before accessing the related memory, so this audit is well worth the time.

Once each lock has a precise specification of what memory it protects, check whether any of the regions protected by different locks overlap. While overlap is not strictly incorrect, it should be avoided because it is not useful for memory associated with two different locks to overlap. Consider what would happen when memory common to both locks needs to be updated. Which one should be taken? The possibilities include the following:

Enter just one of the locks arbitrarily This convention doesn't work because it no longer provides mutual exclusion. It is now possible for two different update sites to choose different locks and thus update the same memory location simultaneously.

Always enter both locks This provides mutual exclusion, but it is twice as expensive, and it offers no advantage over having just one lock for the location.

Consistently enter one of the locks This is the same as saying that only one lock protects the particular location.

How Many Locks?

To illustrate the next point, the example is slightly more complicated. Instead of having just one totalRequests tally, suppose you had two different tallies for high- and low-priority requests. totalRequests is not stored directly, but is calculated as follows:

totalRequests = highPriRequests + lowPriRequests;
The program needs the invariant that the sum of the two global variables be equal to the number of times any thread has serviced a request. Unlike the previous example, this invariant involves two memory locations. This immediately brings up the question of whether you need one lock or two. The answer to this depends on your design goals.

The main advantage of having two locks, one for highPriRequests and another for lowPriRequests, is that it allows more concurrency. If one thread is trying to update highPriRequests and another thread is trying to update lowPriRequests, but there is only one lock, then one thread would have to wait for the other. If there were two locks, then each thread could proceed without contention. In the example, this improvement in concurrency is trivial since a single lock would be taken relatively rarely and would not be held for a long time. Imagine, however, if the lock was protecting a table that was used frequently during the processing of requests. In this case, it may pay to lock only parts of the table (such as the hash bucket entries) so that several threads can access the table simultaneously.

The main disadvantage of having two locks is the complexity involved. The program clearly has more parts associated with it so there is more opportunity for programmer error. This complexity grows quickly with the number of locks in the system, so it is best to have few locks that protect large regions of memory and only split them when lock contention is shown to be a bottleneck on performance.

At the extreme, a program could have a single lock that protects all memory that is reachable from more than one thread. This design would work quite well for the request-processing example if a thread can process a request without needing to access shared data. If processing a request requires a thread to make many updates to shared memory, then a single lock will become a bottleneck. In this case, the single large region of memory protected by the single lock needs to be split into several non-overlapping subsets, each protected by its own lock.

Taking Locks on Reads

Thus far, I've presented the convention that a lock should always be entered before writing a memory location, but I have not discussed what should happen when memory is read. The read case is a little more complicated because it depends on the programmer's expectations. Consider the example again. Let's imagine that you have decided to read highPriRequests and lowPriRequests to compute totalRequests:

totalRequests = highPriRequests + lowPriRequests;
In this case, the expectation is that the values in these two memory locations add up to an accurate total number of requests. This will only be true if they are not changing while this computation is proceeding. If each tally has its own lock, both locks need to be entered before the sum can be computed.

In contrast, the code that increments highPriRequests only needs to take a single lock. This is because the only invariant used by the update code is that highPriRequests be an accurate tally; lowPriRequests is not involved whatsoever. In general, when code needs a program invariant, all locks associated with any memory involved with the invariant must be taken.

A useful analogy that helps illustrate this point is shown in Figure 5. Think of the computer's memory as a slot machine with thousands of windows, one for each memory location. When you start your program it is like pulling the handle of the slot machine. Memory locations start spinning as other threads change the values of memory. When a thread enters a lock, the locations associated with the lock stop spinning because the code consistently follows the convention that a lock is taken before any update is attempted. The thread can repeat this process, taking more locks and causing more memory to freeze until all the memory locations needed by the thread are stabilized. The thread is now in a position to perform the operation without interference from other threads.

Figure 5 Five Steps to Swap Values Protected by Locks
Figure 5 Five Steps to Swap Values Protected by Locks

This analogy is useful in changing the programmer's mindset from believing that nothing changes unless it is explicitly changed to believing that everything changes unless locks are used to prevent it. Adopting this new mindset is the most important piece of advice when building multithreaded applications.

What Memory Needs Lock Protection?

You've seen how to use locks to protect program invariants, but I haven't been precise about what memory needs such protection. An easy (and correct) answer would be that all memory needs to be protected by a lock, but this would be overkill for most apps.

Memory can be made safe for multithreaded use in one of several ways. First, memory that is only accessed by a single thread is safe because other threads are unaffected by it. This includes most local variables and all heap-allocated memory before it is published (made reachable to other threads). Once memory is published, however, it falls out of this category and some other technique must be used.

Second, memory that is read-only after publication does not need a lock because any invariants associated with it must hold for the rest of the program (since the value does not change).

Third, memory that is actively updated from multiple threads generally uses locks to ensure that only one thread has access while a program invariant is broken.

Finally, in certain specialized cases where the program invariant is relatively weak, it is possible to perform updates that can be done without locks. Typically, specialized compare-and-exchange instructions are used. These techniques are best thought of as special, lightweight implementations of locks.

All memory used in a program should fall into one of these four cases. Moreover, the final case is significantly more subtle and error prone, and thus should only be used when performance requirements justify the extra care and risk involved (I will be devoting a future article to this case). Ignoring that case for now, your general rule should be that all program memory should fall into one of three buckets: thread exclusive, read-only, or lock protected.

Methodical Locking

In practice, most nontrivial multithreaded programs are riddled with races. Much of the problem is that programmers are simply unclear about when locks are needed, which I've hopefully now cleared up. This understanding alone is not sufficient, however. It is frightfully easy to make mistakes because it only takes one missed lock to introduce races. You need strong methodologies that help avoid simple-but-common mistakes. However, even the best current techniques require significant care to apply them well.

One of the simplest and most useful techniques for methodical locking is the concept of a monitor. The basic idea is to piggyback on the data abstraction that is already present in an object-oriented design. Consider the example of a hash table. In a well-designed class, it will already be the case that clients only access its internal state by calling its instance methods. If a lock is taken on entry to any instance method, and released on exit, there is a systematic way of ensuring that all accesses to internal data (instance fields) only occur when the lock is held, as shown in Figure 6. Classes that follow this protocol are called monitors.

Figure 6 Using a Monitor class
Figure 6 Using a Monitor class

It is no accident that the name of a lock in the .NET Framework is System.Threading.Monitor. This type was specifically tailored to support the monitor concept. The reason .NET locks operate on System.Object is to make the creation of monitors easier. Effectively, every object has a built-in lock that can be used to protect its instance data. By embedding the body of every instance method in a lock(this) statement, a monitor can be formed. There is even a special attribute, [MethodImpl(MethodImplAttributes.Synchronized)], that can be placed on instance methods to automatically insert the lock(this) statement. Further, .NET locks are reentrant, meaning that the thread that has entered the lock can enter the lock again without blocking. This allows methods to call other methods on the same class without causing the deadlock that would normally occur.

While useful (and easy to write using .NET), monitors are far from a panacea for locking. If they are used indiscriminately, they can result in either too little or too much locking. Consider an application that uses the hash table shown in Figure 6 to implement a higher-level operation called Debit, which transfers money from one account to another. The Debit method uses the Find method on Hashtable to retrieve the two accounts, and the Update method to actually perform the transfer. Because Hashtable is a monitor, the calls to Find and Update are guaranteed to be atomic. Unfortunately, the Debit method needs more than this atomicity guarantee. If another thread updates one of the accounts between the two calls to Update that Debit makes, Debit can be incorrect. The monitor did a fine job of protecting Hashtable within a single call, but an invariant needed over several calls was missed because too little locking was done.

Fixing the race in the Debit method with a monitor can lead to too much locking. What is needed is a lock that protects all the memory that the Debit method uses and can be held for the duration of the method. If you used a monitor to do this, it would be like the Accounts class shown in Figure 7. Each high-level operation like Debit or Credit will take the lock on Accounts before performing its operation, thus providing the needed mutual exclusion. Creating the Accounts monitor fixes the race, but now calls into question the value of the lock on Hashtable. If all accesses to Accounts (and therefore Hashtable) take the Accounts lock, then mutual exclusion for accesses to Hashtable (which is part of Accounts) is already assured. If that is so, then there is no need for the overhead of having a lock for Hashtable. Too much locking was done.

Figure 7 Monitors Needed Only at Top Level
Figure 7 Monitors Needed Only at Top Level

Another important weakness with the monitor concept is that it provides no protection if the class gives out updatable pointers to its data. For example, it is fairly common for methods like the Find on Hashtable to return an object that the caller can then update. Since these updates can happen outside any call to Hashtable , they are not protected by a lock, breaking the protection the monitor was supposed to provide. Finally, monitors simply don't address the more complicated situation when multiple locks need to be taken.

Monitors are a useful concept, but they are just a tool for implementing a well thought out locking design. Sometimes the memory a lock protects naturally coincides with a data abstraction, and a monitor is the perfect mechanism to implement the locking design. However, other times, one lock will protect many data structures, or only parts of a data structure, in which case a monitor is inappropriate. There is no getting around the hard work of precisely defining what locks a system needs and what memory each lock protects. Now let's consider several guidelines that can help you in this design.

Generally speaking, most reusable code (like container classes) should not have locks built into it because it can only protect itself, and it is likely that whatever code uses it will need a stronger lock anyway. The exception to this rule is when it is important that the code works even with high-level program errors. The global memory heap and security-sensitive code are examples of exceptional cases.

It is less error prone and more efficient to have just a few locks that protect large amounts of memory. A single lock that protects many data structures is a good design if it allows the desired amount of concurrency. If the work each of the threads will do doesn't require many updates to shared memory, I would consider taking this principle to the extreme and having one lock that protects all shared memory. This makes the program almost as simple as a sequential program. It works well for applications whose worker threads don't interact much.

In cases where threads read shared structures frequently, but write to them infrequently, reader-writer locks such as System.Threading.ReaderWriterLock can be used to keep the number of locks in the system low. This type of lock has one method for entering for reading and one for entering for writing. The lock will allow multiple readers to enter concurrently, but writers get exclusive access. Since readers now don't block one another, the system can be made simpler (containing fewer locks) and still achieve the necessary concurrency.

Unfortunately, even with these guidelines to help, designing highly concurrent systems is fundamentally harder than writing sequential systems. Lock usage can often be at odds with normal object-oriented program abstraction because locking is really another independent dimension of the program that has its own design criteria (other such dimensions include lifecycle management, transactional behavior, real-time constraints, and exception-handling behavior). Sometimes the needs of locking and the needs of data abstraction align, such as when they both are used to control access to instance data. There are other times, however, when they are in conflict. (Monitors don't usefully nest, and pointers can cause monitors to "leak".)

There is no good resolution to this conflict. The bottom line is that multithreaded programs are more complicated. The trick is to control the complexity. You've already seen one strategy: try to use just a few locks at the top level of your data abstraction hierarchy. Even this strategy can be at odds with modularity because many data structures will likely be protected by one lock. This means that there is no obvious data structure on which to hang the lock. Typically, the lock needs to be a global variable (never a really great idea for read/write data), or part of the most global data structure involved. In the latter case, it must be possible to get at that structure from any other structure that needs the lock. Sometimes this is a hardship because extra parameters might need to be added to some methods, and the design might get a bit messy, but it's better than the alternatives.

When complexity like this starts to show itself in a design, the correct response is to make it explicit, not to ignore it. If some method does not take locks itself, but expects its caller to provide that mutual exclusion, then that requirement is a precondition of calling that method and should be in its interface contract. If, on the other hand, a data abstraction might take a lock or call client (virtual) methods while holding a lock, that also needs to be part of the interface contract. Only by making these details explicit at interface boundaries can you make good decisions locally about the code. In a good design, most of these contracts are trivial. The callee expects the caller to have provided exclusion for the entire data structure involved, so specifying this is not a hardship.

In an ideal world, the complexity of concurrency design would be hidden in a class library. Unfortunately, there is also little that class library designers can do to make the library friendlier to multithreading. As the Hashtable example shows, locking just one data structure is rarely useful. It is only when the threading structure of program is designed that locks can be usefully added. Typically this makes it the application developer's responsibility. Only holistic frameworks like ASP.NET that define the threading structure end-user code plugs into are in a position to relieve their clients from the burden of careful lock design and analysis.

Deadlock

Another reason to avoid having many locks in the system is deadlock. Once a program has more than one lock, deadlock becomes a possibility. For example, if one thread tries to enter Lock A and then Lock B, while simultaneously another thread tries to enter Lock B and then Lock A, it is possible for them to deadlock if each enters the lock that the other owns before attempting to enter the second lock.

From a pragmatic perspective, deadlocks are generally prevented in one of two ways. The first (and best) way to prevent deadlock, is to have few enough locks in the system that it is never necessary to take more than one lock at a time. If this is impossible, deadlock can also be prevented by having a convention on the order in which locks are taken. Deadlocks can only occur if there is a circular chain of threads such that each thread in the chain is waiting on a lock already acquired by the next in line. To prevent this, each lock in the system is assigned a "level", and the program is designed so that threads always take locks only in strictly descending order by level. This protocol makes cycles involving locks, and therefore deadlock, impossible. If this strategy does not work (can't find a set of levels), it is likely that the lock-taking behavior of the program is so input-dependent that it is impossible to guarantee that deadlock can not happen in every case. Typically, this type of code falls back on timeouts or some kind of deadlock detection scheme.

Deadlock is just one more reason to keep the number of locks in the system small. If this cannot be done, an analysis must be made to determine why more than one lock has to be taken simultaneously. Remember, taking multiple locks is only necessary when code needs to get exclusive access to memory protected by different locks. This analysis typically either yields a trivial lock ordering that will avoid deadlock or shows that complete deadlock avoidance is impossible.

The Cost of Locks

Another reason to avoid having many locks in a system is the cost of entering and leaving a lock. The lightest locks use a special compare/exchange instruction to check whether the lock is taken, and if it isn't, they enter the lock in a single atomic action. Unfortunately, this special instruction is relatively expensive (typically ten to hundreds of times longer than an ordinary instruction). There are two main reasons for this expense, and they both have to do with issues arising on a true multiprocessor system.

The first reason is that the compare/exchange instruction must ensure that no other processor is also trying to do the same thing. Fundamentally, this requires one processor to coordinate with all other processors in the system. This is a slow operation and accounts for the lower bound of the cost of a lock (dozens of cycles). The other reason for the expense is the effect inter-process communication has on the memory system. After a lock has been taken, the program is very likely to access memory that may have been recently modified by another thread. If this thread ran on another processor, it is necessary to ensure that all pending writes on all other processors have been flushed so that the current thread sees the updates. The cost of doing this largely depends on how the memory system works and how many writes need to be flushed. This cost can be pretty high in the worst case (possibly hundreds of cycles or more).

Thus the cost of a lock is can be significant. If a frequently called method needs to take a lock and only executes a hundred or so instructions, lock overhead is likely to be an issue. The program generally needs to be redesigned so that the lock can be held for a larger unit of work.

In addition to the raw overhead of entering and leaving locks, as the number of processors in the system grows, locks become the main impediment in using all the processors efficiently. If there are too few locks in the program, it is impossible to keep all the processors busy since they are waiting on memory that is locked by another processor. On the other hand, if there are many locks in the program, it is easy to have a "hot" lock that is being entered and exited frequently by many processors. This causes the memory-flushing overhead to be very high, and throughput again does not scale linearly with the number of processors. The only design that scales well is one in which worker threads can do significant work without interacting with shared data.

Inevitably, performance issues might make you want to avoid locks altogether. This can be done in certain constrained circumstances, but doing it correctly involves even more subtleties than getting mutual exclusion correct using locks. It should only be used as a last resort, and only after understanding the issues involved. I will be devoting a complete article to this in a future issue.

A Quick Word on Synchronization

While locks provide a way of keeping threads out of each other's way, they really don't provide a mechanism for them to cooperate (synchronize). I'll quickly cover the principles for synchronizing threads without introducing races. As you'll see, they are not much different from the principles for proper locking.

In the .NET Framework, synchronization functionality is implemented in the ManualResetEvent and AutoResetEvent classes. These classes provide Set, Reset, and WaitOne methods. The WaitOne method causes a thread to block as long as the event is in the reset state. When another thread calls the Set method, AutoResetEvents will allow one thread that called WaitOne to unblock, while ManualResetEvents will unblock all waiting threads.

Generally, events are used to signal that a more complicated program property holds. For example, a program might have a queue of work for a thread, and an event is used to signal to the thread that the queue is not empty. Note that this introduces a program invariant that the event should be set if and only if the queue is not empty. The rules for proper locking require that if code needs an invariant, there must be locks that provide exclusive access for all memory associated with that invariant. Applying this principle in a queue suggests that all access to the event and the queue should happen only after entering a common lock.

Unfortunately, this design can cause a deadlock. Take this scenario for example. Thread A enters the lock and needs to wait for the queue to be filled (while owning the queue's lock). Thread B, which is attempting to add an entry to the queue that Thread A needs, will try to enter the queue's lock before modifying the queue and thus block on Thread A. Deadlock!

In general, it's a bad idea to hold locks while waiting on events. After all, why lock out all other threads from a data structure when a thread is waiting on something else? You're just asking for deadlocks. A common practice is to release the lock and then wait on the event. However it's now possible for the event and the queue to be out of sync. We have broken the invariant that the event is an accurate indicator of when the queue is not empty. A typical solution is to weaken the invariant in this case to, "if the event is reset, then the queue is empty." This invariant is strong enough that it is still safe to wait on the event without risking waiting forever. This relaxed invariant means that when a thread returns from WaitOne, it cannot assume that the queue has an element in it. The waking thread has to enter the queue's lock and verify that the queue has an element. If not (say some other thread removed the entry), it must wait again. If fairness among threads is important, this solution has a problem, but it does work well for most purposes.

Conclusion

The fundamental principles underlying good coding in sequential and multithreaded programs are not so different. In both cases, the entire code base has to methodically protect the invariants that are needed elsewhere in the program. The difference is that, as Figure 2 shows, protecting program invariants is more complicated in the multithreaded case. As a result it requires a significantly higher degree of discipline to build a correct multithreaded program. Part of this discipline is ensuring through tools like monitors that all thread-shared, read-write data is protected by locks. The other part is carefully designing which memory is protected by which lock, and controlling the extra complexity that locks inevitably add to the program. As in a sequential program, good designs are typically the simplest, and for multithreaded programs this means having the fewest number of locks that achieve the needed concurrency. If you keep the locking design simple and methodically follow your locking design, you can write race-free multithreaded programs.

Vance Morrison is the compiler architect for the .NET runtime at Microsoft, where he has been involved in the design of the .NET runtime since its inception. He drove the design for the .NET Intermediate Language (IL) and was lead for the just-in-time (JIT) compiler team.

© 2007 Microsoft Corporation and CMP Media, LLC. All rights reserved; reproduction in part or in whole without permission is prohibited.


Proxy Detection: Take the Burden Off Users with Automatic Configuration in .NET

New information has been added to this article since publication.
Refer to the Editor's Update below.

Proxy Detection

Take the Burden Off Users with Automatic Configuration in .NET

Durgaprasad Gorti
This article is based on a prerelease version of the .NET Framework 2.0. All information herein is subject to change
This article discusses:
  • Proxy setting configuration and policy
  • Using automatic proxy configuration
  • New features in the .NET Framework 2.0
This article uses the following technologies:
.NET Framework, C#
Code download available at: AutomaticProxyDetection.exe (113KB)
Contents
Your Proxy Settings
Proxy Support in the .NET Framework 1.1
Proxy Support in the .NET Framework 2.0
Proxy Support for ASP.NET Applications
Conclusion

I
nternet Proxy servers are deployed in most environments for security, performance, and usage tracking purposes. When your application makes a request to a resource on the Internet, the request is often routed through a proxy. The proxy server then makes the request on your behalf and forwards the response back to you. This means that your application needs to know what proxy server to use when requesting a resource on the Internet. You can either use an explicit proxy server name or take advantage of the automatic proxy detection capabilities.

In this article, I will discuss how automatic proxy detection works and how it is supported in the Microsoft® .NET Framework 2.0. I will also discuss how the .NET Framework 2.0 supports connection-specific proxy settings and how to configure your ASP.NET applications to take advantage of the automatic proxy detection found there. I'll show that proxy support in the .NET Framework 2.0 is substantially improved and, in most cases, proxy configuration becomes much easier than ever.

Your Proxy Settings

The .NET Framework gets the proxy settings from the same location in the registry that Microsoft Internet Explorer uses to store proxy settings. Proxy settings are usually per user and thus are stored under the user-specific registry hive (HKCU). If you look under the registry key HKEY_CURRENT_USER\Software\Microsoft\Windows\ CurrentVersion\Internet Settings\Connections, you will find one or more values of type REG_BINARY. The value name indicates the connection name and the binary value contains the proxy settings to be used for that connection. If you take a look at Figure 1 you'll see the relevant portion of the registry on my computer. This information is all editable by a user through the dialog exposed in Internet Explorer from Tools | Internet Options | Connections.

Figure 1 Proxy Settings Registry Key
Figure 1 Proxy Settings Registry Key

Although proxy settings are usually set per user, you can set them per machine by setting a policy. (This would, of course, make the settings the same for all users on that machine.) You can find all the details for this policy at HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\ CurrentVersion\Internet Settings\ProxySettingsPerUser. The type of the value is DWORD. By setting the value to 0 you can then force the proxy settings to be applied per machine. In this case, the proxy settings come from HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\ CurrentVersion\Internet Settings\Connections, just like its HKCU counterpart.

Automatic configuration is a two-step process. First, the user agent (Internet Explorer or the .NET Framework) tries to discover the URL of a configuration script. Typically, the configuration script is a JavaScript file that contains logic to determine whether to use a proxy and what proxy to use for a given target resource. The protocol for automatic detection is documented at Web Proxy Auto-Discovery Protocol. The user agent first tries a DHCPINFORM message. The reply to this message contains the URL of the configuration script. If that doesn't work, a DNS lookup for a well-known alias, WPAD, is issued. Then the configuration script's location is determined as http://<machine>/wpad.dat where the machine name is the result of the DNS lookup. You can try this in your own network. If you open Internet Explorer and go to http://wpad/wpad.dat, you might see the configuration script. If you don't see this, it might be because the network administrator uses custom configuration files with the Internet Explorer Administration Kit (IEAK) or some other nonstandard configuration.

The second step is to download the configuration script from the URL and run the script. The specification of what the configuration script looks like is at Navigator Proxy Auto-Config File Format. Basically, the script has one entry point function: FindProxyForURL. You pass the URL into the function as well as the host to which you would like to connect, and the function returns one or more proxy servers to use. The user agent should try the proxy servers in the order returned. Note that the user agent can get different script content each time the script is downloaded. This might happen because the network administrator has updated the script to reflect the new proxy servers she has added to the pool of proxy servers. The script can contain logic to determine what proxy to use based on the day, time, subnet of the client machine, and the target URL. For example, the following script returns DIRECT (bypass proxy) for the 198.95.*.* subnet, proxy1 for connecting to www.example.com, and proxy2 and proxy3 for connecting to any other resource:

FindProxyForURL(url, host) {
    if (isInNet(host, "198.95.0.0", "255.255.0.0")) {
        return "DIRECT";
    }
    if(host == "www.example.com") {
        return "PROXY proxy1;DIRECT";
    }
    return "PROXY proxy2:8080;PROXY proxy3:8081"
}

By selecting automatic configuration, user agents detect the configuration script URL, download and run the script to automatically determine what proxy to use. If you already know the configuration script URL and don't want auto-detection, in the LAN Settings dialog you can enter the URL and check "Automatic Configuration Script." If you check both the "Automatically Detect Settings" and "Automatic Configuration Script" boxes, auto-detection takes precedence. You can see that a lot of stuff happens behind the scenes to make automatic configuration work. The benefit is that no manual proxy configuration of clients is required.

Proxy Support in the .NET Framework 1.1

The .NET Framework 1.1 shipped with basic proxy support through the System.Net.WebProxy and System.Net.GlobalProxySelection classes. Configuration support is provided through the <defaultProxy> section under <system.net> node in the configuration file. When you install the .NET Framework 1.1, the Machine.config file has the following configuration:

<system.net>
    <defaultProxy>
        <proxy usesystemdefault="true" />
    </defaultProxy>
</system.net>
This means that when you access the GlobalProxySelection.Select property, the proxy instance returned will contain the settings from the registry. As I mentioned earlier, the registry location is the same location as the one Internet Explorer uses and the settings can come from either HKCU or HKLM (for the local machine) depending on the policy.

One of the issues in the .NET Framework 1.1 proxy support is that when the registry is read, the automatic configuration part of the proxy settings is ignored. Only the static portion of the proxy settings are read. However, if the default proxy settings in Internet Explorer are configured for automatic detection and static settings are not populated, the result is that Internet Explorer seems to download the resources correctly, while applications using the .NET Framework 1.1 get an exception that reads, "The underlying connection was closed: The remote name could not be resolved." What is really happening is that the GlobalProxySelection returns an empty proxy due to missing information in the static portion of the proxy settings. The WebRequest class then tries to connect to the resource directly (bypassing any proxy) to the target resource. Since external names can't be resolved in a protected network, you get a "name not resolved" exception thrown back at you.

A lot of users observed this issue indirectly through the Add Web Reference dialog in Visual Studio® .NET 2003 as shown in Figure 2. On the left side is an Internet Explorer browser control that can automatically detect proxy settings and is able to display the Web methods properly. On the right side, the .NET Framework 1.1 code uses only the static settings and isn't able to retrieve the Web Services Description Language (WSDL) for the Web Service, so it fails to list the Web methods. In order to fix this issue, you have three options. The first is to create a WebProxy instance with a specific proxy name and assign it to the WebRequest.Proxy property. The second option is to enter the static settings in Internet Explorer, so the .NET Framework will read those settings. The third option is to use the configuration (either Machine.config or App.config) file to specify a proxy.

Figure 2 Add Web Reference Dialog Doesn't Show Web Methods
Figure 2 Add Web Reference Dialog Doesn't Show Web Methods

The other issue with .NET Framework 1.1 proxy support is that it always read the default LAN connection proxy settings and ignored the dial-up and Virtual Private Network (VPN) connection settings. In other words, only the DefaultConnectionSettings value is read irrespective of the current active connection. Imagine a scenario in which you are using a remote access connection to your corporate network from home. When you are connected to the corporate network, your requests should be routed through the proxy in your corporate network. But since the .NET Framework 1.1 always uses the default LAN connection settings, proxy settings for the VPN connection are not picked up and the app will fail to connect to the target resource.

Long-running applications have another issue with .NET Framework 1.1. Imagine that the static proxy you entered in Internet Explorer is down and your friendly helpdesk person gave you another proxy name to try. After you enter the new proxy name, you would expect your managed app to pick up the new proxy settings. But it doesn't. Once the GlobalProxySelection class reads the proxy settings, they are cached per app domain, and they are not refreshed. The only solution is to reload the app domain, which in most applications means you need to restart the app.

Many users wonder why their code that calls a Web service works fine on a console but doesn't work when they drop it in an .aspx page. The answer is that the ASP.NET account runs under a different user account than the interactive user. Since there is no easy way to edit the proxy settings for the ASP.NET user in the registry, you need to enter the proxy settings explicitly in the Web.config file to make it work properly.

Proxy Support in the .NET Framework 2.0

Fear not, however—the .NET Framework 2.0 significantly enhances proxy support. All of the issues I've mentioned with the .NET Framework 1.1 version are resolved in the new version. In most of the cases, you don't need to think about the proxy configuration any more, which is always a good thing. Let's drill down a little further, though.

Automatic proxy configuration is now fully supported. The .NET Framework 2.0 reads the complete settings from the registry, including the automatic configuration flags and any explicitly provided script location. Automatic detection happens by first using the DHCPINFORM message and then using the DNS resolution techniques already described. Once the script location is identified, the script is downloaded, compiled into an in-memory assembly, and then executed in a sandbox app domain where minimal permissions are granted.

In fact, no code access security (CAS) permissions are granted to the script other than the execution permission. Minimal permissions mean that rogue scripts can't do anything significant other than call the few functions exposed to the script. For a detailed sample of how to host and run your own scripts in a sandbox app domain, see the sidebar, "Hosting the Proxy Script."

Failover is also built in. If auto-detection fails and if you have entered an explicit script location, then that script is downloaded and run. If both fail and if you have a static proxy, then the static proxy is used as a fallback. If all fail, a direct connection to the target URL is attempted.Hosting the Proxy Script

The Microsoft Active Scripting architecture provided a very successful extensibility platform for applications. An application could expose an object model and host a script engine, which would enable end users to write scripts to customize and extend the application functionality. The Active Scripting architecture is COM-based. For the .NET Framework, script hosting is provided through Visual Studio for Applications (VSA). VSA provides a set of classes and interfaces to host managed scripting languages like JScript® .NET and Visual Basic® .NET.

The .NET Framework 2.0 uses VSA and the JScript .NET engine to host proxy scripts. Since this is a managed framework, scripts are compiled into an in-memory assembly. A script is executed every time an application tries to connect to a resource and needs to determine what proxy to use. The downloadable code that accompanies this article contains a sample of how the proxy script is hosted and run, but I will walk you through the relevant portions of the code now:

   m_JScriptEngine = new Microsoft.JScript.Vsa.VsaEngine();
   m_JScriptEngine.RootMoniker = "com.example.wpad://ScriptSample";
   m_JScriptEngine.Site = new VsaEngineSite();
   m_JScriptEngine.InitNew();
   m_JScriptEngine.RootNamespace = "__ProxyScriptSample";

The first thing to do is to create a scripting engine. Once a script engine is created, set the RootMoniker. The RootMoniker must be in the form <protocol>://<path> where the <protocol> string is guaranteed to be unique to the host, and <path> is the unique sequence of characters recognized by the host (the <protocol> element can't be a registered protocol handler such as http: or ftp:). Once the engine is created, you can set the implementation of the IVsaSite using the Site property. I'll take a look at the IVsaSite implementation shortly:

   //Turn off print statements
   m_JScriptEngine.SetOption("print", false);
   //Turn off optimizations
   m_JScriptEngine.SetOption("fast", false);
   //Turn off auto references
   m_JScriptEngine.SetOption("autoref", false);

Once the engine is created and initialized, you can set specific options through the SetOption methods. Here you turn off print statements and optimizations. The autoref option is also turned off to prevent JScript .NET from automatically referencing assemblies based on import statements or type annotations. It is important that you do this because the proxy configuration script is not supposed to access any objects or assemblies other than a handful of functions exposed to the script:

   IVsaCodeItem codeItem = m_JScriptEngine.Items.CreateItem("SourceText",
       VsaItemType.Code, VsaItemFlag.None) as IVsaCodeItem;
   codeItem.SourceText = code.ToString();
   //Add the object model
   m_JScriptEngine.Items.CreateItem("__ScriptObjectModel",
       VsaItemType.AppGlobal, VsaItemFlag.None);

These lines add the script code to the engine and add a global object to the script. The engine will use the IVsaSite implementation to get a reference to the global object when it is first referenced by the script code. The corresponding portion of the IVsaSite implementation is shown here:

   public class VsaEngineSite : IVsaSite {
       ...
       public object GetGlobalInstance(string name) {
          //When the engine asks for __ScriptObjectModel, return an instance
          //of the ScriptObjectModel
           if (name == "__ScriptObjectModel") {
                    return new ScriptObjectModel();
           }
           throw new VsaException(VsaError.GlobalInstanceInvalid);
       }
   ...
   }

The ScriptObjectModel is shown here:

   public class ScriptObjectModel {
       public bool isPlainHostName(string hostName) {
           if (hostName == null) {
                   throw new ArgumentNullException("hostName");
           }
           return hostName.IndexOf('.') == -1;
       }
   }

I implemented only one of the functions exposed to the script, although the others are similarly easy to implement. The function isPlainHostName simply returns true if there are no "." characters, or false otherwise. This function can be used to test if the target is an intranet host or not.

In order to ensure that you truly run the script in a sandbox environment, you should run the script in its own app domain with a restricted set of permissions. In fact, the script needs no permission other than execution. Figure A shows the code that sets the policy for the AppDomain. This policy grants only execution permission to any assembly that includes SandboxEvidence and full trust to all other assemblies. The SandboxEvidence is nothing more than an empty class that serves the purpose of tagging the assembly produced by compiling the script:

   private class SandboxEvidence
   {
   }

The following lines of code show how to include the SandboxEvidence to the assembly produced by compiling the script:

   Evidence engineEvidence = new Evidence();
   //Required because the appdomain policy is intersected with the other
   //levels. Assembly also gets execute permissions at all other levels
   engineEvidence.AddHost(new Zone(SecurityZone.Intranet));
   //Add SandboxEvidence to force execute only at the AppDomain level
   engineEvidence.AddHost(new SandboxEvidence());
   m_JScriptEngine.Evidence = engineEvidence;

If you don't include the SandboxEvidence, the assembly will get full trust as per the app domain policy, so it is important to add this evidence.

With the tightened security policy on the app domain and hosting the script in that app domain, you can make sure that the script runs in a least-privileged environment. Download the code for this article and see how this is all put together. Then save the following sample proxy script to a file and run the code to see the script host in action:

function FindProxyForURL(url, host) {
    if(isPlainHostName(host)) {
        return "DIRECT";
    }
    else {
        return "PROXY LOCALHOST:8080";
    }
}

In the .NET Framework 2.0, a new property, DefaultWebProxy, has been added to the WebProxy class. [ Editor's Update - 8/19/2005: DefaultWebProxy is a new property on the WebRequest class, not on the WebProxy class.] This property provides functionality that is similar to the GlobalProxySelection.Select property in version 1.1. If you don't explicitly assign any proxy on your Web request, the proxy obtained from the WebProxy.DefaultWebProxy is used. The GlobalProxySelection class is deprecated, as the WebProxy.DefaultWebProxy is more intuitive and discoverable than the GlobalProxySelection.Select. In the .NET Framework 2.0, GlobalProxySelection.Select is rewired to return the same result as WebRequest.DefaultWebProxy property. This means that in the .NET Framework 2.0, GlobalProxySelection.Select can return a proxy that supports automatic configuration. If you run applications from version 1.1 on version 2.0, by default you get the automatic proxy configuration support without having to change your code.

Let's look at an example. Save the following JScript code as wpad.js under the wwwroot directory:

function FindProxyForURL(url, host)
{
    if(host == "www.example.com")
        return "PROXY MyProxy:9090";
    else
        return "DIRECT";
}
Make sure that you can download this file from the URL http://localhost/wpad.js. This is your proxy configuration script. Now configure the proxy settings. For this example, enter http://localhost/wpad.js into the address textbox and select Use Automatic Configuration Script. Also, make sure that you uncheck the Automatically Detect Settings box in your browser's settings. If you keep it checked, auto-detection takes precedence and your sample configuration script will not be used. Then you need to open the Internet Options window and select use automatic configuration script. In addition, select manual proxy settings and enter "MyProxy" for proxy address and 80 for port.

Now save the following C# code as GetProxy.cs and compile and run it on the .NET Framework in both versions 1.1 and 2.0:

using System;
using System.Net;
public class Test {
    public static void Main(string[] args)     {
        IWebProxy iwp11 = GlobalProxySelection.Select;
        Console.WriteLine(iwp11.GetProxy(new Uri(args[0])).ToString());
        //comment the following 2 lines when compiling on 1.1
        IWebProxy iwp20 = WebRequest.DefaultWebProxy;
        Console.WriteLine(iwp20.GetProxy(new Uri(args[0])).ToString());
    }
}

It helps if you have a side-by-side installation of both versions so that you don't need to switch machines. Also, make sure that you comment out the lines that use DefaultWebProxy when you are compiling against the .NET Framework 1.1.

Figure 3 Using Automatic Configuration
Figure 3 Using Automatic Configuration

When you run the previous code on the .NET Framework 2.0 using the settings shown in Figure 3

GetProxy.exe http://www.example.com
prints the following:
http://myproxy:9090
http://myproxy:9090
This is because the proxy script returns http://myproxy:9090 for the host www.example.com. Likewise
GetProxy.exe http://www.microsoft.com
would print the following:
http://www.microsoft.com/
http://www.microsoft.com/
In this case, the proxy script is returning "DIRECT," which is another way of saying "bypass proxy." The .NET Framework 1.1 returns the same URL for the proxy as the target resource when bypassing proxy, so this behavior is continued in the .NET Framework 2.0 for backward compatibility.

When you run this code on the .NET Framework 1.1 (after commenting out the lines specific to version 2.0) the program returns http://myproxy for all hosts, indicating that the automatic configuration is not used in the .NET Framework 1.1.

The .NET Framework 2.0 supports connection-specific proxy settings. Whenever proxy settings are read, the currently active connection on that machine is detected and proxy settings for that specific connection are read. For example, when you're using a remote access connection to the corporate network, the proxy settings specific to that remote access connection are used. When you disconnect the remote access connection upon completion, the proxy settings for the default LAN connection are used. In addition, a very cool feature in the .NET Framework 2.0 is that you don't need to restart the app to get the proxy settings refreshed when you establish a new connection or disconnect. The .NET Framework automatically detects remote access connect-and-disconnect events and refreshes the internal proxy settings, making your applications more robust without you having to write additional code.

For your long-running applications, I have good news too. Whenever the proxy settings for a connection change, you automatically refresh the settings internally by detecting a change in the registry values. What this means is that if you update the proxy settings in Internet Explorer, the new settings are picked up automatically. Once again, you don't need to restart the application.

Proxy Support for ASP.NET Applications

If you have written any code in ASP.NET to connect to an external resource or invoke an external Web service, you've probably seen an application that works perfectly outside of ASP.NET yet fails inside ASP.NET. You might check the code again and again and get the same poor result before you realize that the ASP.NET code is running under a different user than you, the interactive user. For Windows® XP, by default, the ASP.NET worker process runs under the user ASPNET, and on Windows Server™ 2003, by default, the Network Service account is used. When you try to call an external Web service from your .aspx pages, which proxy is used? You can't just open Internet Explorer and look at the proxy settings since that displays the settings stored under your HKCU node, not the information relevant to the ASPNET or Network Service account. If you're curious, you can open the following key to look at the Network Service proxy settings:

HKEY_USERS\S-1-5-20\Software\Microsoft\Windows\CurrentVersion\
Internet Settings\Connections
For ASP.NET users, there is no well-known security identifier (SID). You can run the following code (requiring the .NET Framework 2.0) to determine the SID for the ASPNET account:
using System;
using System.Security.Principal;
class Test {
    static void Main(string[] args) {
        NTAccount account = new NTAccount(args[0]);
        Console.WriteLine(account);
        SecurityIdentifier sid = (SecurityIdentifier)
            account.Translate(typeof(SecurityIdentifier));
        Console.WriteLine(sid);
    }
}

On my machine, if I call this with the argument, %COMPUTERNAME%\aspnet, it prints:

S-1-5-21-1957994488-1085031214-725345543-1003
Once I know the SID, I can look up the proxy settings under the path, HKEY_USERS\<SID>\ Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections. Even if you can peek at the proxy settings for these users, you can't edit them easily because they're binary values.

One simple solution is to set the policy on the machine to make the proxy settings per machine instead of per user. In this case, any user who opens the Internet Options dialog will see and edit the machine-wide proxy settings (provided they have permissions to read and write to the HKLM). Your ASP.NET application will pick up the machine-wide proxy settings and things should work consistently. This simple solution may not work if you must support user-specific proxy settings or when you don't have control over where your application eventually gets deployed.

A better solution may be to use the Web.config file to specify the proxy settings for your ASP.NET applications. Using the <system.net><defaultProxy> section, you can specify a proxy server to be used. This is supported in the .NET Framework 1.1 and 2.0. But what if you don't want to specify a specific proxy but rather want to take advantage of the automatic detection? In the final release of the .NET Framework 2.0, configuration for support for proxy is enhanced in order to support automatic detection. (Please note that this is not part of the .NET Framework Beta 2 release.)

Of course, none of this touches on impersonation. What if you have an ASP.NET application that impersonates authenticated users? As I mentioned before, if no proxy is specified, the default proxy used is the WebRequest.DefaultWebProxy. This property returns the proxy for the user that the process is running under (assuming a default configuration), specifically ignoring the current thread token. This ensures that the default proxy returns consistent settings irrespective of the current thread token.

What if you want the proxy settings for the currently impersonated user? Let's just say, I hope you don't need this. When a user is impersonated, the profile for that user is not typically loaded. That means the HKCU for that user is not available. In a service-oriented application, you don't want to load profiles for each impersonated user because it can become very expensive very quickly. I strongly encourage you to look at other design options. If you must access the proxy settings of the currently impersonated user you can use the WebProxy.GetSystemWebProxy method. This method internally calls the RegOpenCurrentUser API to load the user-specific registry hive. It does not load the user profile, however, and there is currently no managed API to load user profiles. Instead you'll need to use P/Invoke to load the user profile.

If you successfully load the user profile, the proxy settings for that user are read. If the profile is not loaded, the RegOpenCurrentUser returns a handle to HKEY_USERS\.Default hive.

Conclusion

Automatic proxy configuration makes applications more robust by easing the burden put on end users to make sure their Internet settings are correct. The .NET Framework 2.0 fully supports automatic proxy configuration. In addition, by supporting connection-specific proxy settings and automatically refreshing proxy settings whenever the active connection changes, the .NET Framework 2.0 offers a solution to all the major proxy issues that many developers encountered with .NET Framework 1.1.

Durgaprasad Gorti is a test lead at Microsoft for the managed network programming APIs. He can be reached at dgorti@microsoft.com.


Data Binding: Give Your Everyday Custom Collections a Design-Time Makeover

Data Binding

Give Your Everyday Custom Collections a Design-Time Makeover

Paul Ballard
Parts of this article are based on a prerelease version of the .NET Framework 2.0. Those sections are subject to change.
This article discusses:
  • Creating a type-safe custom collection
  • Implementing design-time support
  • Data binding support implementation
  • Generics in the .NET Framework 2.0
This article uses the following technologies:
Visual Basic .NET, .NET Framework
Code download available at: CollectionsandDataBinding.exe (158KB)
Contents
Why Use Custom Collections?
Student Business Object
Creating a Type-Safe Custom Collection
Implementing Design-Time Support for the Collection
Adding Data Binding Support
Implementing Editing Support
Adding Sorting Support
Adding Searching Support
Using the StudentCollection
Getting Your Collection Ready for Prime Time
Looking Forward to the .NET Framework 2.0
Conclusion

I
n the long-running debate over whether to use ADO.NET DataSets or custom business objects to represent data within a .NET-based application, DataSets have always held a couple of strategic advantages over custom objects: data binding and design-time support. Rather than engage in that debate, which would take an entire article in itself, I'm going to show how you can level the playing field by providing data binding support for custom collections to enable sorting, searching, and editing in as simple a manner as possible. I will also show how to make all of these features available in the Windows® and Web Forms Designers, just like an ADO.NET DataSet. Lastly, I'll take a close look at new functionality available in the Microsoft® .NET Framework 2.0 and then show how much easier it is to implement the same functionality using generics.

Why Use Custom Collections?

When working with data in a .NET-based application, it's often useful to create lists of objects. The .NET Framework provides standard list and dictionary types such as ArrayList and Hashtable that allow you to organize objects of any type into collections. You can then bind these collections to Windows Forms or ASP.NET controls at run time. However, because these collections are built to hold any type of object, there is no way to enforce a rule that limits a collection to containing only objects of a specific type. For example, you cannot define an ArrayList that only contains objects of type Order. Because of this, when developers use the list they have to check or cast the types being returned from the collection to ensure the object is of the type they are expecting. If this check isn't done and an object of the wrong type was added to the collection, exceptions can result. Custom collections—specialized classes based on the .NET Framework collections—let you enforce type safety by limiting collections to accepting only objects of a specific type. This in turn makes the collection easier to use.

Unfortunately, none of the .NET Framework collection classes that you can use to build your custom collection provide the level of design-time data binding support that the DataSet provides. By implementing a few interfaces, however, you can provide that level of functionality to your collection.

Student Business Object

For the examples, I've created a business object called Student which has properties for FirstName, LastName, Age, and Grade. Figure 1 shows the source for this class. It includes a default public constructor as well as an overloaded constructor for creating an instance and setting its properties immediately. Also, you can see that there is a PropertyChanged event defined for each property and that this event is raised in the corresponding property's Set block. These are good basic business object design patterns.

Figure 1 The Student Class (Close Figure 1)
Public Class Student

    Private m_FirstName As String
    Private m_LastName As String
    Private m_Grade As Integer
    Private m_Age As Integer

    Public Event FirstNameChanged As System.EventHandler
    Public Event LastNameChanged As System.EventHandler
    Public Event GradeChanged As System.EventHandler
    Public Event AgeChanged As System.EventHandler

    Public Sub New()
    End Sub

    Public Sub New(ByVal FirstName As String, ByVal LastName As String, _
                   Optional ByVal Grade As Integer = 0, _
                   Optional ByVal Age As Integer = 0)
        With Me
            .m_FirstName = FirstName
            .m_LastName = LastName
            .m_Grade = Grade
            .m_Age = Age
        End With
    End Sub

    Public Property FirstName() As String
        Get
            Return m_FirstName
        End Get
        Set(ByVal Value As String)
            m_FirstName = Value
            RaiseEvent FirstNameChanged(Me, EventArgs.Empty)
        End Set
    End Property

    Public Property LastName() As String
        Get
            Return m_LastName
        End Get
        Set(ByVal Value As String)
            m_LastName = Value
            RaiseEvent LastNameChanged(Me, EventArgs.Empty)
        End Set
    End Property

    Public Property Grade() As Integer
        Get
            Return m_Grade
        End Get
        Set(ByVal Value As Integer)
            m_Grade = Value
            RaiseEvent GradeChanged(Me, EventArgs.Empty)
        End Set
    End Property

    Public Property Age() As Integer
        Get
            Return m_Age
        End Get
        Set(ByVal Value As Integer)
            m_Age = Value
            RaiseEvent AgeChanged(Me, EventArgs.Empty)
        End Set
    End Property

End Class

Creating a Type-Safe Custom Collection

Now, let's create a custom collection that enforces type safety by only allowing objects of type Student to be added to the list. When you create a custom list collection, follow the naming standard of adding the word "Collection" after the object type. In the case of Student, the collection would be called StudentCollection.

There are several ways to create a custom collection. You can create a class that derives directly from one of the standard collection classes, such as ArrayList, and then hide the methods that are not type safe and properties that reference items in the list as Object using the Shadows (Visual Basic®) or new (C#) keyword and replace them with your type-specific versions. You can also create a class that simply implements the IList interface, leaving the internal storage of items in the list entirely up to you, but requiring a bit more code. The preferred method, however, is to derive the custom collection from the CollectionBase class.

The CollectionBase class implements many of the methods and properties that a collection might need, such as Clear and Count, but it leaves the specification of the various type-specific methods to the developer. These methods include Add, Remove, and Insert, as well as the indexer property Item. The CollectionBase class includes a protected property called List, which returns a reference to the CollectionBase's IList implementation (this wraps an internal ArrayList that is not type safe). In the implementation of the public type-specific methods for your collection, you simply wrap the methods of the CollectionBase.List property that aren't type safe with the publicly exposed type safe version. As an example, the StudentCollection.Add method would look like this:

Public Sub Add(ByVal Student As Student)
    Me.List.Add(Student)    'List.Add() takes type Object
End Sub

Figure 2 shows the implementation of the StudentCollection class that ensures that the collection only holds objects of type Student. Note that the Item property uses the Default keyword. This makes it easier for developers who use the collection to access items in the list using the array indexing syntax, as demonstrated in the following line of code:

Dim FirstStudent As Student = myStudentCollection(3) ' get fourth item

Figure 2 The StudentCollection Class (Close Figure 2)
Imports System.Collections

Public Class StudentCollection
    Inherits CollectionBase

    Public Sub New()
    End Sub

    Public Sub Add(ByVal Student As Student)
        Me.List.Add(Student)    'List.Add() takes type Object
    End Sub

    Public Function Contains(ByVal Student As Student) As Boolean
        Return Me.List.Contains(Student)
    End Function

    Public Function IndexOf(ByVal student As Student) As Integer
        Return Me.List.IndexOf(student)
    End Function

    Public Sub Insert(ByVal index As Integer, ByVal Student As Student)
        Me.List.Insert(index, Student)
    End Sub

    Default Public Property Item(ByVal index As Integer) As Student
        Get
            Return CType(Me.List.Item(index), Student)
        End Get
        Set(ByVal Value As Student)
            Me.List(index) = Value
        End Set
    End Property

    Public Sub Remove(ByVal Student As Student)
        Me.List.Remove(Student)
    End Sub

End Class

CollectionBase also implements a couple of very important interfaces, the first of which is called IEnumerable. The IEnumerable interface methods allow developers to work with the items in the collection using a For Each loop. It also implements the IList interface. This interface lets the runtime and the designers know that the class contains the standard list methods such as Add, Remove, and Count that are necessary for data binding. Any object that implements the IList interface can be bound to list-using UI controls, such as Datagrids, Listboxes, and Comboboxes. Therefore, the code presented thus far is all that is necessary to provide a type-safe collection that can be one-way bound at run time. But what about design time?

Implementing Design-Time Support for the Collection

The first step in making the StudentCollection just as useful as a DataSet is to make it available to the Forms Designer. The Forms Designer only allows certain objects to be dragged and dropped onto the design surface or the form itself. Those objects must implement the IComponent interface. Most of the controls you see in the toolbox derive from System.Windows.Forms.Control, which in turn derives from System.ComponentModel.Component. The Component class implements the IComponent interface. Rather than force StudentCollection to derive from Control or Component in order to make use of the Component class's implementation of IComponent, it's easier and cleaner to simply implement the interface directly.

IComponent is a simple interface that defines a property called Site, which returns an object that implements the ISite interface. An object that implements ISite contains a reference to a component and the container it is in and is used by the Designer to keep track of object hierarchies. In the case of the StudentCollection, you will be passed a reference to an ISite object and need only store it for use in the Site property. The IComponent interface also exposes a Disposed event and derives from IDisposable, requiring the implementation of a Dispose method. The Dispose method allows the component to free up any resources currently being used when the object is no longer needed. The StudentCollection will clear its internal list and then raise the Disposed event. Figure 3 shows the ICollection implementation for StudentCollection.

Figure 3 ICollection Implementation (Close Figure 3)
Private m_Site As ISite = Nothing

Public Event Disposed(ByVal sender As Object, _
    ByVal e As System.EventArgs) Implements _
        System.ComponentModel.IComponent.Disposed

Public Property Site() As System.ComponentModel.ISite _
        Implements System.ComponentModel.IComponent.Site
    Get
       Return m_Site
    End Get
    Set(ByVal Value As System.ComponentModel.ISite)
        m_Site = Value
    End Set
End Property

Public Sub Dispose() Implements System.IDisposable.Dispose
    Me.List.Clear()
    If Not m_Site Is Nothing AndAlso Not m_Site.Container Is Nothing Then
        m_Site.Container.Remove(Me)
    End If
    RaiseEvent Disposed(Me, System.EventArgs.Empty)
End Sub

Unfortunately, Visual Studio® .NET 2002 and Visual Studio .NET 2003 are not quite clever enough to know to add a component from the project to the toolbox palette automatically. Therefore, you have to add the component to the toolbox manually by right-clicking the toolbox and selecting Add/Remove Items. Using the Add/Remove Items Dialog Box, you can browse to the assembly containing your component and it will be added to the toolbox. You can then drag it from the toolbox and drop it onto your forms. You'll see that it adds the component to the bottom of the designer, and in the source code for the form it adds code to create and store the component, just as if it were a Button or TextBox control.

Adding Data Binding Support

To enable your collection to have design-time data binding, you next need to implement the IBindingList interface. The IBindingList interface defines the methods and properties necessary for a list-based UI control like the DataGrid to be able to add and remove items from the list, sort the list based on the selected column, and search for a value in the list. Figure 4 shows the complete list of properties and methods that define the IBindingList interface.

Figure 4 IBindingList Interface (Close Figure 4)
Public Interface IBindingList
    Implements IList, ICollection, IEnumerable

    ' Events
    Event ListChanged As ListChangedEventHandler

    ' Methods
    Sub AddIndex(ByVal [property] As PropertyDescriptor)
    Function AddNew() As Object
    Sub ApplySort(ByVal [property] As PropertyDescriptor, _
        ByVal direction As ListSortDirection)
    Function Find(ByVal [property] As PropertyDescriptor, _
        ByVal key As Object) As Integer
    Sub RemoveIndex(ByVal [property] As PropertyDescriptor)
    Sub RemoveSort()

    ' Properties
    ReadOnly Property AllowEdit As Boolean
    ReadOnly Property AllowNew As Boolean
    ReadOnly Property AllowRemove As Boolean
    ReadOnly Property IsSorted As Boolean
    ReadOnly Property SortDirection As ListSortDirection
    ReadOnly Property SortProperty As PropertyDescriptor
    ReadOnly Property SupportsChangeNotification As Boolean
    ReadOnly Property SupportsSearching As Boolean
    ReadOnly Property SupportsSorting As Boolean
End Interface

The first set of properties to implement is the AllowNew, AllowEdit, and AllowRemove properties. These are simple read-only Boolean values that tell the UI control whether a user can add, edit, or remove items from the list using the UI control at run time.

The next set of properties is SupportsChangeNotification, SupportsSearching, and SupportsSorting. These properties tell the UI control what features of the IBindingList interface this collection supports. Returning True from the SupportsChangeNotification property tells the user interface control that any changes to the collection will cause the ListChanged event to be raised. This event is part of the IBindingList interface, and the user interface control listens for this event to signal that it should redraw the data if it has changed. The last property, IsSorted, will be covered during the Sorting implementation.

Implementing Editing Support

When the collection is bound to a control, such as a DataGrid, and the collection supports editing via the AllowEdit property, a user can change the values of an item's properties directly from the control. The functionality is similar to that of a spreadsheet. To support this requires code in both the StudentCollection and the Student object. To support row-based editing from within a data-bound control, the Student class must implement the IEditableObject interface.

IEditableObject defines three methods: BeginEdit, EndEdit, and CancelEdit. The BeginEdit method is called when the user navigates to that item within the control, but only when editing is allowed by the collection. EndEdit is called when the user navigates away from that item and any changes the user made should be saved. CancelEdit is called when the user navigates away and the changes should be discarded. Supporting edited data will require the Student object to track the state of its properties before and after editing has begun.

The BeginEdit method in Figure 5 shows an implementation that first checks to see that BeginEdit hasn't already been called. This is an important step because BeginEdit is called many times during the initialization of data binding as well as during navigation of the control by the user. The next thing it does is create a new Hashtable to store the original values of its properties. It then uses a helper class in the ComponentModel namespace to create a PropertyDescriptorCollection. This collection will contain a list of PropertyDescriptor objects that describe each property of the Student class. The BeginEdit method then iterates through the list adding the value of each property to the Hashtable using the corresponding PropertyDescriptor as an index. This means of storing the data is very generic and could have been more easily implemented by just copying the values of the properties to an ArrayList. However, this example will continue to work if you add or remove properties from the Student class as well as if you choose to move this code into an EditableObject base class from which you will derive all of your business objects.

Figure 5 Student's IEditableObject Implementation (Close Figure 5)
Private m_OriginalData As Hashtable
Private m_Editing As Boolean = False
Private m_IsAddNew As Boolean = False

Public Event CancelAddNew( _
        ByVal sender As Student, ByVal Remove As Boolean)

Public Sub New(ByVal IsAddNew As Boolean)
    m_IsAddNew = IsAddNew
End Sub

Public Sub BeginEdit() Implements IEditableObject.BeginEdit
    If Not m_Editing Then
        m_Editing = True
        m_OriginalData = New Hashtable
        Dim Properties As PropertyDescriptorCollection = _
            TypeDescriptor.GetProperties(Me)
        For Each prop As PropertyDescriptor In Properties
            m_OriginalData.Add(prop, prop.GetValue(Me))
        Next
    End If
End Sub

Public Sub EndEdit() Implements IEditableObject.EndEdit
    If m_Editing Then
        If (m_IsAddNew) Then
            RaiseEvent CancelAddNew(Me, False)
        End If
        m_OriginalData = Nothing
        m_Editing = False
    End If
End Sub

Public Sub CancelEdit() Implements IEditableObject.CancelEdit
    If m_Editing Then
        If (m_IsAddNew) Then
            RaiseEvent CancelAddNew(Me, True)
        Else
            Dim prop As PropertyDescriptor
            For Each entry As DictionaryEntry In m_OriginalData
                prop = CType(entry.Key, PropertyDescriptor)
                prop.SetValue(Me, entry.Value)
            Next
            m_OriginalData = Nothing
            m_Editing = False
        End If
    End If
End Sub

Dealing with items that are added via a user is a bit tricky and requires some coordination between the collection and the object created. If the user adds a Student to the StudentCollection using a DataGrid for example, and then cancels the edit, you need to remove the item from the collection manually. To handle this, you'll need to add a bit more code to the Student class implementation. First, the class needs another overloaded constructor that takes a Boolean value. This constructor will be called when the user chooses to add an item to the collection via the IBindingList.AddNew method. The Boolean value tells the instance of the Student class that it was created at the user's request. The value is stored as m_IsAddNew and then, in the CancelEdit method, if the m_IsAddNew field is true, the method raises an event that you define called CancelAddNew. This event signals to the collection that the item should be removed from the list. Figure 5 shows the Student class implementation of IEditableObject including the CancelAddNew functionality.

The EndEdit method checks to ensure that the object was being edited and, if so, checks to see if this object was added as a result of the UI control. If it was, it raises the CancelAddNew event passing False to tell the collection that the addition of the item is complete and not to remove it from the collection. It then discards the original property values and sets the m_Editing flag to false.

The CancelEdit method replaces the object's current property values with the values stored in the Hashtable. To do this, the method iterates over the items in the Hashtable and uses the PropertyDescriptor to set the properties value back to the values stored in the Hashtable and then sets the Hashtable to Nothing.

In the StudentCollection, you have two methods to implement in order to wrap up editing support. The IBindingList.AddNew method is called when the user creates a new item in the collection via the UI control. As mentioned earlier, you can't simply add and return a new object in case the user changes her mind. The method creates a new Student object using the overloaded constructor passing in the value of True and then adds the Student to its internal list. It then adds an event handler to be called by that Student object's CancelEdit method and finally returns the new Student object. The CancelAddNew method unwires the event handler and based on the Remove argument, will remove the item from the collection. Figure 6 shows the implementation of these two StudentCollection class methods.

Figure 6 IBindingList Editing Methods (Close Figure 6)
Protected Function AddNew() As Object Implements IBindingList.AddNew
    Dim s As New Student(True)
    Me.Add(s)
    AddHandler s.CancelAddNew, AddressOf Me.CancelAddNew
    Return s
End Function

Friend Sub CancelAddNew(ByVal student As Student, ByVal Remove As Boolean)
    RemoveHandler student.CancelAddNew, AddressOf Me.CancelAddNew
    If (Remove) Then Me.List.Remove(student)
End Sub

Protected Overrides Sub OnInsertComplete(ByVal index As Integer, _
        ByVal value As Object)
    MyBase.OnInsertComplete(index, value)
    RaiseEvent ListChanged(Me, _
        New ListChangedEventArgs(ListChangedType.ItemAdded, index))
End Sub

Protected Overrides Sub OnRemoveComplete(ByVal index As Integer, _
        ByVal value As Object)
    MyBase.OnRemoveComplete(index, value)
    RaiseEvent ListChanged(Me, _
        New ListChangedEventArgs(ListChangedType.ItemDeleted, index))
End Sub

The IBindingList interface defines an event that should be raised whenever a change is made in the list. It's called, appropriately enough, ListChanged. The ListChanged event takes a ListChangedEventArgs parameter that includes an enumeration specifying what type of change caused the event to be raised as well as the index of the item that was inserted, deleted, modified, or moved. Implementing change notification is simple, thanks to the CollectionBase class's OnInsertComplete and OnRemoveComplete overrides. The StudentCollection implementation is shown in Figure 6.

Adding Sorting Support

When sorting a list, the collection needs a way to perform a comparison of two objects that tells the sorting algorithm whether the first value is higher than, lower than, or equal to the second. With primitive types like String and Int32, this is trivial. But sorting business objects can require complex comparisons that involve the application of business rules and validations. In order to implement a more complex comparison for business objects, you should create a new class that implements the IComparer interface. This class can contain specific code for comparing the two business objects. The IComparer interface only defines one method, Compare. The Compare function takes two Objects as parameters and returns an integer value representing the relationship of Object x to Object y. The value should be 1 if x is greater than y, -1 if x is less than y, and 0 if they are the same.

In the implementation of an IComparer, the first thing to do is check to make sure that the parameters are of the type you expect. In the case of StudentComparer, it expects two parameters of type Student. If anything else is received, an ArgumentException is thrown. It then calls the private CompareProperties method, which does the real work of comparing the Students. The CompareProperties method creates an integer used to change the sign of the result based on whether the sort is ascending or descending. The comparison is then done based on the m_SortProperty in an If/Then/ElseIf structure, with the result being multiplied by the direction modifier. For example, if the value of the sort property on Object x is less than the value of the same property on y, the result is -1. If the order of sorting is ascending, that value is multiplied by 1, returning -1. This will cause the sorting algorithm to place y higher in the list than x. However, if the sort direction is descending, then the result of the comparison is multiplied by -1, resulting in a positive 1. This will cause the sorting algorithm to put Object x higher on the list than Object y.

Figure 7 shows the implementation of the StudentComparer class that can be used to compare Students based on any property. It derives from IComparer and implements the Compare method, as mentioned earlier. Notice that it has a constructor that takes the PropertyDescriptor for the property to sort on and the direction of the sort as parameters. These are used in the Compare method to determine the relationship between the two Students. I should also point out (at the risk of being pummeled by e-mails pointing out my dependency on Visual Basic helper methods), that I am intentionally turning off Option Strict for this class. This is to avoid doing type comparison checks when the values of the properties are compared at run time. This was done solely to keep the code as brief as possible and is by no means a recommendation.

Figure 7 Student Comparison Based on Property (Close Figure 7)
Option Strict Off

Imports System.Collections
Imports System.Collections.Specialized
Imports System.ComponentModel

Public Class StudentComparer
    Implements IComparer

    Private m_SortProperty As PropertyDescriptor
    Private m_SortDirection As ListSortDirection

    Public Sub New(ByVal SortProperty As PropertyDescriptor, _
            ByVal direction As ListSortDirection)
        m_SortProperty = SortProperty
        m_SortDirection = direction
    End Sub

    Private Function CompareProperties( _
            ByVal x As Student, ByVal y As Student) As Integer

        Dim result As Integer = 0
        Dim directionModifier As Integer

        If (m_SortDirection = ListSortDirection.Ascending) Then
            directionModifier = 1
        Else
            directionModifier = -1
        End If

        If (x Is Nothing) Then
            result = -1 * directionModifier
        ElseIf (y Is Nothing) Then
            result = 1 * directionModifier
        ElseIf (m_SortProperty.GetValue(x) < _
                m_SortProperty.GetValue(y)) Then
            result = -1 * directionModifier
        ElseIf (m_SortProperty.GetValue(x) > _
                m_SortProperty.GetValue(y)) Then
            result = 1 * directionModifier
        Else
            result = 0
        End If

        Return result
    End Function

    Public Function Compare(ByVal x As Object, ByVal y As Object) _
            As Integer Implements IComparer.Compare

        If (Not TypeOf x Is Student) Then Throw New ArgumentException( _
            "Argument must be of Type Student", "x")
        If (Not TypeOf y Is Student) Then Throw New ArgumentException( _
            "Argument must be of Type Student", "y")

        Return CompareProperties(CType(x, Student), CType(y, Student))
    End Function
End Class

To allow sorting of the collection via the UI controls, your collection must implement the IBindingList.ApplySort method. This method receives a PropertyDescriptor describing the property used to sort the list and an enumeration indicating the order of the sort—either ascending or descending. These values are assigned to private fields that are exposed through the IBindingList.SortDirection and IBindingList.SortProperty properties. In order to restore the original order of the items in the list if a sort is removed, the original order needs to be saved. The easiest way to do this is to create a new ArrayList based on the original list before you call the method that actually changes the list's order. Figure 8 shows the ApplySort implementation for the StudentCollection class.

Figure 8 ApplySort Implementation (Close Figure 8)
Private m_SortProperty As PropertyDescriptor
Private m_SortDirection As ListSortDirection
Private m_OriginalList As ArrayList

Protected ReadOnly Property SortDirection() As ListSortDirection _
        Implements IBindingList.SortDirection
    Get
        Return m_SortDirection
    End Get
End Property

Protected ReadOnly Property SortProperty() As PropertyDescriptor _
    Implements System.ComponentModel.IBindingList.SortProperty
    Get
        Return m_SortProperty
    End Get
End Property

Protected ReadOnly Property IsSorted() As Boolean _
        Implements IBindingList.IsSorted
    Get
        Return m_SortProperty Is Nothing
    End Get
End Property

Private Sub SaveList()
    m_OriginalList = New ArrayList(Me.List)
End Sub

Private Sub ResetList(ByVal NewList As ArrayList)
    Me.List.Clear()
    Me.InnerList.AddRange(NewList)
End Sub

Private Sub DoSort()
    Me.InnerList.Sort( _
        New StudentComparer(m_SortProperty, m_SortDirection))
End Sub

Protected Sub ApplySort(ByVal [property] As PropertyDescriptor, _
        ByVal direction As ListSortDirection) _
        Implements IBindingList.ApplySort
    m_SortProperty = [property]
    m_SortDirection = direction
    If (m_OriginalList Is Nothing) Then SaveList()
    DoSort()
    RaiseEvent ListChanged(Me, _
        New ListChangedEventArgs(ListChangedType.Reset, 0))
End Sub

Protected Sub RemoveSort() Implements IBindingList.RemoveSort
    ResetList(m_OriginalList)
    m_SortDirection = Nothing
    m_SortProperty = Nothing
    RaiseEvent ListChanged(Me, _
        New ListChangedEventArgs(ListChangedType.Reset, 0))
End Sub

There are lots of ways to implement the actual sorting algorithm. Rather than reinvent the wheel, I've chosen to take advantage of ArrayList's built-in sorting implementation. If you remember, the CollectionBase.List property exposes an IList implementation that wraps an internal ArrayList. That internal ArrayList is exposed from another property on CollectionBase, InnerList. As such, I simply call the InnerList's Sort method, passing in an instance of a StudentComparer. The IBindingList.RemoveSort method restores the list to its original order, which was saved before the list was sorted for the first time. It's important to remember to raise the ListChanged event when the list order is changed to keep the UI controls in sync. The last property to implement for sorting is IBindingList.IsSorted. This property is largely unused but easy enough to implement by returning False if the IBindingList.SortProperty is Nothing and True if it has been set.

Adding Searching Support

User interaction isn't the only thing that affects the binding of UI controls to a collection. If a developer using your collection sets the SelectedValue or SelectedIndex of a UI control that is bound to the collection, the control must be able to find that value in the collection and make it the current item. To provide this feature, implement the IBindingList.Find method. This method receives a PropertyDescriptor defining which property is being used by the control for its SelectedValue. This is usually defined with the DataMember property on the control. The IBindingList.Find method also receives the value to find in the collection. It then iterates through the list and checks the value of the specified property to see if it matches the value passed in the parameter. When you have a match, you return the index of the item in the collection. Figure 9 shows this process implemented for the StudentCollection class, which includes some specific type-checking of the value to ensure that you properly match the value to the key.

Figure 9 Find Property (Close Figure 9)
Protected Function Find(ByVal [property] As PropertyDescriptor, _
        ByVal key As Object) As Integer Implements IBindingList.Find
    Dim idx As Integer
    Dim result As Integer = -1
    For idx = 0 To Me.List.Count - 1
        Dim value As Object = [property].GetValue(Me.List(idx))
        Select Case [property].Name
            Case "FirstName", "LastName"
                If (value Is key) Then result = idx
            Case "Age", "Grade"
                If (CType(value, Integer) = CType(key, Integer)) _
                    Then result = idx
            Case Else
                If (value Is key) Then result = idx
        End Select
        If (result <> -1) Then Return result
    Next
End Function

Protected Sub AddIndex(ByVal [property] As PropertyDescriptor) _
        Implements IBindingList.AddIndex
    'Not implemented
End Sub

Protected Sub RemoveIndex(ByVal [property] As PropertyDescriptor) _
        Implements IBindingList.RemoveIndex
    'Not Implemented
End Sub

There are two other methods of the IBindingList I haven't covered here: AddIndex and RemoveIndex. These methods are used specifically with ADO.NET DataSets and are not relevant for a collection. Therefore, I'll leave the implementation of AddIndex and RemoveIndex blank.

Using the StudentCollection

Figure 10 shows a Windows Form with a DataGrid and several fields for displaying and editing a Student object. All of these controls can be bound to a StudentCollection class within the designer. First, you'll drag a StudentCollection from the toolbox and drop it onto the form, which will move to the bottom of the Designer. If you look at the properties of the collection you can see that it doesn't actually expose much; most of its magic is in what it does to the other controls on the form. To bind the collection to the DataGrid, you'll set the DataGrid's Data source property to the collection. Notice that when you drop down the list for this property, the collection is displayed as an option. This is because the collection, via CollectionBase, implements the IList interface. When you select the collection, you'll see the columns added to the DataGrid for each property. To bind the individual UI controls to properties of the currently selected Student item, expand the control's DataBindings property in the Properties window. For the TextBox controls, you should see the Text property in the list. Drop down the list and you'll see a hierarchical view of the Student class as shown in the properties window in Figure 10. Select the property of the Student class you want to bind to. Other UI controls have different properties in their list for you to bind to, but the collection should always appear as an option. Execute the application and you'll be adding and editing items in the collection without writing any additional code in your forms.

Figure 10 Student Object
Figure 10 Student Object

Getting Your Collection Ready for Prime Time

While all of the necessary steps to provide design-time data-binding support have been implemented, if you compile the source code as defined thus far you'll find one particularly annoying problem. The properties listed for the Student class aren't displayed in the DataGrid UI control in the same order that you defined them. While this is generally acceptable for controls such as a ListBox or ComboBox that don't display multiple properties of an item from the collection at the same time, it is frustrating on a DataGrid.

I'm going to go one step beyond what is necessary and implement the ITypedList interface. This interface is used to allow a type to explicitly specify the properties it exposes as opposed to letting the TypeDescriptor class figure it out using reflection. The interface defines two methods: GetItemProperties and GetListName. The GetItemProperties method has no parameters and returns a PropertyDescriptorCollection. In this method, you can call TypeDescriptor.GetProperties on the Student class to load all or some of the properties from your type. Once you have the collection created, you can add and remove items or sort the list. The Sort method for this collection has an overload that takes an array of strings, where each element in the array is a name of a property in the collection. The order in which the names appear in the array will be the order that the properties will display in the resulting collection. Figure 11 shows the StudentCollection's implementation of ITypedList.GetItemProperties returning the properties in the order in which they are defined. The GetListName returns a string value with a name for the list.

Figure 11 ITypedList.GetItemProperties (Close Figure 11)
Protected Function GetItemProperties(ByVal listAccessors() _
    As PropertyDescriptor) As PropertyDescriptorCollection _
        Implements ITypedList.GetItemProperties

    Dim m_OriginalList As PropertyDescriptorCollection =
        TypeDescriptor.GetProperties(GetType(Student))
    Dim m_SortedList As PropertyDescriptorCollection = _
        m_OriginalList.Sort(New String() { _
            "FirstName", "LastName", "Age", "Grade"})
    Return m_SortedList
End Function

Protected Function GetListName(ByVal listAccessors() As
    PropertyDescriptor) As String _
        Implements System.ComponentModel.ITypedList.GetListName
    Return "StudentCollection"
End Function

Another nice feature to add to the collection is an icon to be displayed in the toolbox palette. This will help developers distinguish the collection control from other objects. To add an icon for your collection, you should add a 16?16 bitmap to the assembly containing the collection class and using the same file name as the collection but with a .bmp extension. For example, the icon for the StudentCollection class is contained in the StudentCollection.bmp file. In the properties window for the bitmap file, make sure that the Build Action is set to "Embedded Resource". This will put the image into the assembly's resource list. After adding the icon to the assembly, when you add the collection component to the toolbox it will display the icon you defined next to the name of the collection.

Looking Forward to the .NET Framework 2.0

With the final release of the .NET Framework 2.0 just around the corner, it makes sense to consider what changes in the .NET Framework will have in store for building custom collections and data binding. The first thing you should know is that no changes are necessary to migrate your collections from the .NET Framework version 1.x to version 2.0. The IComponent, IBindingList, and ITypedList interfaces haven't changed. There have been quite a few new features added, though, both for creating type-safe collections and for data binding.

There have been significant improvements made to data binding in the designers for the new version. In fact, a developer who is creating an application that uses a custom business object like the Student class shown in previous examples can bind that object directly to the new DataGridView control without creating a collection at all.

From the DataGridView's Tasks window, click "Add a Data Source". This will start the Data Source Configuration Wizard, which allows you to add a data source to your project from a database, a Web service, or another object. Select "Object" and the wizard displays a screen listing the classes in your project and any classes that your project references. When you select the Student class, a new BindingSource object is added to the designer. You should also see that the DataGridView displays the columns matching the properties of the Student class.

Behind the scenes of BindingSource is a new type of collection called a generic collection. A generic collection is a collection in which the type of the items that the collection contains is defined when the collection is instantiated. You will no longer need to implement type-safe methods and properties to add and retrieve items from the collection because the .NET Framework takes care of that for you. There are several types of generic collections ranging from simple lists, such as List(Of T), to specialized generics like Stack(Of T) and Queue(Of T). One of these specialized collections is the BindingList(Of T). This is a generic collection that implements the IBindingList interface. The BindingSource control automatically creates a BindingList(Of Student) when you specify the Student class as the data source in the Data Source Wizard. The DataGridView is then bound to the BindingSource, taking most of the work out of creating a type-safe collection that can be bound to controls at run time.

In order to get true parity with the custom collection built in the first part of this article, you have to do a bit more coding. While BindingList(Of T) implements the IBindingList interface, it does not automatically implement the sorting features. It sets the IBindingList.SupportsSorting property to False and if the IBindingList.ApplySort method is somehow called, it throws a NotSupportedException. You can change that by deriving a custom collection from BindableList(Of T) and then overriding the ApplySortCore method as well as the SupportsSortingCore, SortPropertyCore, and SortDirectionCore properties. You can use exactly the same sorting logic as in the .NET Framework 1.x implementation, including the StudentComparer class. Figure 12 shows a type-safe custom collection for the .NET Framework 2.0 that overrides the BindableList's sorting methods to enable sorting of the list. You'll notice that you have to write considerably less code than you wrote before.

Figure 12 Sorting the List (Close Figure 12)
Imports Students.Student
Imports System.ComponentModel

Public Class StudentCollection
    Inherits BindingList(Of Student)
    Implements IComponent

#Region "IComponent Implementation"
    Private m_Site As ISite = Nothing

    Public Event Disposed(ByVal sender As Object, _
        ByVal e As System.EventArgs) _
        Implements System.ComponentModel.IComponent.Disposed

    Protected Property Site() As System.ComponentModel.ISite Implements _
            System.ComponentModel.IComponent.Site
        Get
            Return m_Site
        End Get
        Set(ByVal Value As System.ComponentModel.ISite)
            m_Site = Value
        End Set
    End Property

    Public Sub Dispose() Implements System.IDisposable.Dispose
        Me.Items.Clear()
        RaiseEvent Disposed(Me, System.EventArgs.Empty)
    End Sub
#End Region

#Region "IBindingList Sorting Features"
    Private m_SupportsSorting As Boolean = True
    Private m_SortProperty As PropertyDescriptor
    Private m_SortDirection As ListSortDirection
    Private m_OriginalList As ArrayList

    Protected Overrides ReadOnly Property SupportsSortingCore() As Boolean
        Get
            Return m_SupportsSorting
        End Get
    End Property
    Protected Overrides ReadOnly Property SortDirectionCore() As _
        System.ComponentModel.ListSortDirection
        Get
            Return m_SortDirection
        End Get
    End Property

    Protected Overrides ReadOnly Property SortPropertyCore() As _
        System.ComponentModel.PropertyDescriptor
        Get
            Return m_SortProperty
        End Get
    End Property

    Protected Overrides ReadOnly Property IsSortedCore() As Boolean
        Get
            Return m_SortProperty Is Nothing
        End Get
    End Property

    Private Sub SaveList()
        m_OriginalList = New ArrayList(Me.Items)
    End Sub

    Private Sub ResetList(ByVal NewList As ArrayList)
        Me.Items.Clear()
        Me.InnerList.AddRange(NewList)
    End Sub

    Private Sub DoSort()
        Me.InnerList.Sort( _
            New StudentComparer(m_SortProperty, m_SortDirection)
    End Sub

    Protected Overrides Sub ApplySortCore( _
            ByVal prop As PropertyDescriptor, _
            ByVal direction As ListSortDirection)
        m_SortProperty = prop
        m_SortDirection = direction
        If (m_OriginalList Is Nothing) Then
            SaveList()
        End If
        DoSort()
    End Sub

    Protected Overrides Sub RemoveSortCore()
        ResetList(m_OriginalList)
        m_SortDirection = Nothing
        m_SortProperty = Nothing
    End Sub
#End Region
End Class

Once again, the order of the columns in the DataGridView doesn't match the order in which the Student class defines the properties. You can implement the ITypedList interface if you like, but one nice feature of the DataGridView control is that it will allow you to change the order of the columns from within the designer. This makes the implementation of ITypedList not strictly necessary. In order to view the collection in the designer, you do have to implement the IComponent interface again as well and you can reuse the code from the .NET Framework 1.x version verbatim. Unlike Visual Studio .NET 2003, however, Visual Studio 2005 is clever enough to find the component and put it into the toolbox palette automatically.

You can now set the BindingSource.DataSource property to point to an instance of the custom collection and you'll be able to sort the data just as before.

Conclusion

In this article I've shown that all of the powerful data binding features currently available for DataSets can also be made available to custom collections by implementing a couple of interfaces. While it's not necessarily simple, type-safe collections that can be used from within the designer and which allow sorting, editing, and searching are a valuable tool to have in your development arsenal. The .NET Framework 2.0 and Visual Studio 2005 make the use of business objects even easier than before. While this won't eliminate the use of DataSets, it's always good to have more options.

Paul Ballard is a Visual Basic MVP, MCSD, MCAD, and MCSE certified consultant and the President of The Rochester Consulting Partnership Inc. He has spent more than 15 years designing and building client/server and Web-based distributed applications and is currently specializing in Microsoft .NET technologies as a consultant, speaker, and trainer.


Winsock: Get Closer to the Wire with High-Performance Sockets in .NET

New information has been added to this article since publication.
Refer to the Editor's Update below.

Winsock

Get Closer to the Wire with High-Performance Sockets in .NET

Daryn Kiely
Parts of this article are based on a prerelease version of the .NET Framework 2.0. Those sections are subject to change.
This article discusses:
  • Basic socket programming
  • Factors in creating scalable socket-based servers
  • The System.Net.Sockets namespace
  • Debugging socket-based applications
This article uses the following technologies:
C#, .NET Framework
Code download available at: HighPerformanceSockets.exe (169KB)
Contents
Sockets Primer
Making Connections
Server Overview
Threaded Server
Using Select for Multiplexed I/O
Asynchronous I/O
What About Scalability?
Socket Client Applications
What's Next?
Debugging a Sockets Application

S
ockets are the transport mechanism most frequently used in high-performance server applications. Fortunately, the Win32® Windows® Sockets library (Winsock) provides mechanisms to improve the performance of programs that use sockets, and the Microsoft® .NET Framework provides a layer over Winsock so that managed applications can communicate over sockets (see Figure 1). So much advanced socket support is great, but using all these layers to write a truly high-performance socket-based application requires a little background information.

Figure 1 Windows Sockets Layers
Figure 1 Windows Sockets Layers

I am going to write a trivial chat server app to explore methods for writing a socket-based server and client using the base System.Net.Sockets.Socket class. Although .NET provides higher-level abstractions like the TcpListener and TcpClient classes (also in System.Net.Sockets), these classes are missing some of the advanced features exposed by the lower-level Socket class. That said, they can be useful in many situations. The TcpListener class provides simple methods that listen for and accept incoming connection requests in blocking synchronous mode, while the TcpClient class provides simple methods for connecting, sending, and receiving stream data over a network in synchronous blocking mode.

Sockets Primer

Sockets define the endpoints for communication, typically across networks. A socket can be used to talk over any of a number of protocols. The most common protocols today are User Datagram Protocol (UDP) and Transmission Control Protocol (TCP).

UDP sockets are connectionless and are generally used for broadcast and multicast communications. UDP has no provision for reliable transport or ordering of messages. It is up to the end application to detect and handle packet loss and packet ordering.

TCP sockets are connection-oriented and provide a reliable communication path between two endpoints. The big advantage of TCP is that it ensures message delivery and proper message ordering. I will focus on TCP sockets in this article.

TCP sockets can either be client or server. A server socket waits for clients to connect; a client socket initiates a connection. Once sockets are connected, both clients and servers can send and receive data or close communications.

To set up a TCP server socket in managed code, the first step is to create an instance of the Socket class. Socket's constructor accepts three parameters: an AddressFamily, a SocketType, and a ProtocolType. The AddressFamily indicates the addressing scheme used by this socket. The two most common AddressFamily values used are InterNetwork for IPV4 addresses and InterNetworkV6 for IPV6 addresses. The SocketType indicates the type of communication that will take place over the socket, the two most common types being Stream (for connection-oriented sockets) and Dgram (for connectionless sockets). The ProtocolType specifies which protocol the socket uses and can have values such as Tcp, Udp, Idp, Ggp, and a host of others. As an example, to create a socket for TCP communication, you could instantiate a Socket as follows:

Socket s = new Socket(
     AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

Once a socket is created, it can be bound to an address. Binding is optional for client sockets but necessary for server sockets. To bind a socket to an address, call the Socket's Bind method. Bind needs to know the address and port that will be associated with the socket, and thus the method accepts as a parameter an instance of a class that derives from EndPoint. Most of the time, this will be an instance of the IPEndPoint class (the only other EndPoint-derived class included in the Framework is the IrDAEndPoint, which is used for infrared port communication).

The IPEndPoint is a logical representation of the endpoint for network communications; it consists of an IP address and a port. The constructor for an IPEndPoint takes the IP address and port number. The port number is simply an integer representing the port to use, while the IP address can be represented as a .NET IPAddress class or as a long integer (where each of the segments of the IP address are one of the bytes in the integer). There are several predefined IPAddresses available through static properties on the IPAddress class; the two most useful addresses for TCP-based sockets are IPAddress.Loopback and IPAddress.Any.

IPAddress.Loopback is a well-known address (127.0.0.1) that is local to the current machine. This is a network pseudo-address that does not rely on hardware or any network connectivity. It allows testing to be done locally without having to worry about the actual network or networking hardware. This is the same address that's used when you use "localhost" as the host name in a URL, although that can be changed by editing the hosts file at %windir%\system32\drivers\etc.

IPAddress.Any (0.0.0.0) tells a server to listen for client communications on all of the network interfaces rather than just the interface associated with a particular IP address (UDP behaves slightly differently than TCP in this regard). Why wouldn't you use this all the time? First, you need to be able to control who connects to your server. If you have multiple network interface cards (NICs) in a server, a situation referred to as a dual-homed server, you can listen for connections on one of the interfaces (for instance, on your corporate LAN), but not on the other (such as the NIC hooked up to the Internet), by specifying which network address you want to listen on.

The second reason is security. If your socket is set up to reuse an address (meaning that the socket can be bound to an address that is already in use), and someone else connects to the same port in use by your server application, the underlying socket subsystem must decide which bound socket will receive a packet coming in on that port. Typically the server application that is more tightly bound to the port (that specified a more specific address in the call to Bind) gets the data. The System.Net.NetworkInformation namespace contains classes that provide detailed information about each of the network interfaces in the system. The following code will print out each of the unicast IP addresses for each NIC:

foreach (NetworkInterface nic in
    NetworkInterface.GetAllNetworkInterfaces())
{
    Console.WriteLine(nic.Name);
    foreach (UnicastIPAddressInformation addrInfo in
        nic.GetIPProperties().UnicastAddresses)
    {
        Console.WriteLine("\t" + addrInfo.Address);
    }
}
To bind to a particular NIC, you can retrieve the IP address information for that NIC and use that address in the IPEndPoint.

Making Connections

An application may want to get an address for a server so it can either make a connection to it or bind to the address. One way to get the address of a server is through the System.Net.Dns class. The Dns class provides forward lookup services for IP addresses and reverse lookup services for host names. It consists of static members that perform network address and name resolution. The Dns.GetHostEntry method returns an IPHostEntry that contains lists of all the relevant information about a machine. It can list all the IP addresses the machine has published to the DNS server. For simplicity, in the examples in this article I'll just use the first address in the list.

After Bind is called, the Socket.Listen method sets up internal queues for a socket. Whenever a client attempts to connect, the connection request is put into a queue. The Listen method takes one argument, the maximum number of socket connections that can be in the pending state. The Socket.Accept method pulls the first pending request from the queue and returns a new Socket instance that can be used for communication with the client.

For a client socket, after creating the Socket instance you typically call the Connect method. You can also first use the Bind method if you require your client to use a specific port. If you do not bind your client socket, the Connect method automatically chooses a port for you. The call to Connect will attempt to establish a connection to the server. Connect also takes an EndPoint, which is used to locate the target remote host. Once a connection is established, either end can use the Send and Receive methods to pass data back and forth.

After a connection is established and the applications are finished communicating over the socket, you must close the socket. The Shutdown method is used to politely initiate a socket closure. This allows all the unsent data to be sent and the unreceived data to be received. If you are synchronously reading data from a socket and you receive 0 bytes, the peer has closed the socket.

Figure 2 summarizes the steps needed to create a server socket and a client socket. As you can see, socket basics are actually fairly simple; what's harder is making socket applications perform well.

Figure 2 Standard Socket Calls for Clients and Servers (Close Figure 2)

Server | Client
Socket.Socket | Socket.Socket
Socket.Bind | Socket.Bind (optional)
Socket.Listen | Socket.Connect
Socket.Accept |
Socket.Read/Socket.Write | Socket.Read/Socket.Write
Socket.Shutdown (optional) | Socket.Shutdown (optional)
Socket.Close | Socket.Close

When a Socket method encounters a network error, a SocketException is thrown. The exception wraps the error code that was passed up from Win32. For those of you familiar with Win32 Winsock development, SocketException.ErrorCode is the same error code you would have received by calling the WSAGetLastError function in the Winsock library. The key Winsock error codes you should be familiar with are listed in the chart in Figure 3. In the .NET Framework 2.0, SocketException.SocketErrorCode provides the same error information through a value from the SocketError enumeration.

Figure 3 Important Error Codes (Close Figure 3)

Error Number | Winsock Value | SocketError Value | Meaning
10004 | WSAEINTR | Interrupted | The system call was interrupted. This can happen when a socket call is in progress and the socket is closed.
10048 | WSAEADDRINUSE | AddressAlreadyInUse | The address you are attempting to bind to or listen on is already in use.
10053 | WSACONNABORTED | ConnectionAborted | The connection was aborted locally.
10054 | WSAECONNRESET | ConnectionReset | The connection was reset by the other end.
10061 | WSAECONNREFUSED | ConnectionRefused | The connection was refused by the remote host. This can happen if the remote host is not running or if it is unable to accept a connection because it is busy and the listen queue is full.

Server Overview

The initialization of a server socket involves creating the socket, binding it to an address, and setting up the listen queue. Each of the different forms of servers that as I am going to write will utilize the same code to initialize the socket, as shown in Figure 4.

Figure 4 Server Socket Setup (Close Figure 4)
private Socket _serverSocket;

private void SetupServerSocket()
{
    // Resolving local machine information
    IPHostEntry localMachineInfo =
        Dns.GetHostEntry(Dns.GetHostName());
    IPEndPoint myEndpoint = new IPEndPoint(
        localMachineInfo.AddressList[0], _port);

    // Create the socket, bind it, and start listening
    _serverSocket = new Socket(myEndpoint.Address.AddressFamily,
        SocketType.Stream, ProtocolType.Tcp);
    _serverSocket.Bind(myEndpoint);
    _serverSocket.Listen((int)SocketOptionName.MaxConnections);
}

I am going to start with a basic threaded server and explore the limitations of the Socket class. Because the basic API consists of blocking calls, when writing a threaded service you need to spawn threads for accepting connections and performing socket I/O.

There are two ways to avoid creating separate threads for dealing with server sockets. The first is the traditional way of dealing with multiple I/O streams in a single thread—the Select method. The second way is to use asynchronous I/O. Both of these methods for writing a server will be explored in more detail next.

Threaded Server

The threaded server requires you to manage threads that deal with the I/O for all the sockets. The first consideration is the call to Accept. In a synchronous server, the call to Accept must run in its own thread to ensure that sockets are accepted in a timely manner. Figure 5 shows an example for spawning a new thread to deal with accepting socket connections.

Figure 5 Simple Threaded Server (Close Figure 5)
class ThreadedServer
{
    private Socket _serverSocket;
    private int _port;

    public ThreadedServer(int port) { _port = port; }

    private class ConnectionInfo
    {
        public Socket Socket;
        public Thread Thread;
    }

    private Thread _acceptThread;
    private List<ConnectionInfo> _connections =
        new List<ConnectionInfo>();

    public void Start()
    {
        SetupServerSocket();
        _acceptThread = new Thread(AcceptConnections);
        _acceptThread.IsBackground = true;
        _acceptThread.Start();
    }

    private void SetupServerSocket()
    {
        // Resolving local machine information
        IPHostEntry localMachineInfo =
            Dns.GetHostEntry(Dns.GetHostName());
        IPEndPoint myEndpoint = new IPEndPoint(
            localMachineInfo.AddressList[0], _port);

        // Create the socket, bind it, and start listening
        _serverSocket = new Socket(myEndpoint.Address.AddressFamily,
            SocketType.Stream, ProtocolType.Tcp);
        _serverSocket.Bind(myEndpoint);
        _serverSocket.Listen((int)SocketOptionName.MaxConnections);
    }

    private void AcceptConnections()
    {
        while (true)
        {
            // Accept a connection
            Socket socket = _serverSocket.Accept();
            ConnectionInfo connection = new ConnectionInfo();
            connection.Socket = socket;

            // Create the thread for the receives.
            connection.Thread = new Thread(ProcessConnection);
            connection.Thread.IsBackground = true;
            connection.Thread.Start(connection);

            // Store the socket
            lock (_connections) _connections.Add(connection);
        }
    }

    private void ProcessConnection(object state)
    {
        ConnectionInfo connection = (ConnectionInfo)state;
        byte[] buffer = new byte[255];
        try
        {
            while (true)
            {
                int bytesRead = connection.Socket.Receive(buffer);
                if (bytesRead > 0)
                {
                    lock (_connections)
                    {
                        foreach (ConnectionInfo conn in _connections)
                        {
                            if (conn != connection)
                            {
                                conn.Socket.Send(
                                    buffer, bytesRead, SocketFlags.None);
                            }
                        }
                    }
                }
                else if (bytesRead == 0) return;
            }
        }
        catch (SocketException exc)
        {
            Console.WriteLine("Socket exception: " + exc.SocketErrorCode);
        }
        catch (Exception exc)
        {
            Console.WriteLine("Exception: " + exc);
        }
        finally
        {
            connection.Socket.Close();
            lock (_connections) _connections.Remove(connection);
        }
    }
}

As you can see, once the socket is accepted, a new thread is spawned to do the receiving for that socket. This thread does nothing but receive data from the socket and process it (which in this case means sending it out to all the other connected sockets). In a sense, the example in Figure 5 is a very basic chat server. To test it, just create a simple .NET Framework 2.0 console application with the following code in the Main method:

class Server {
  static void Main(string [] args) {
    ThreadedServer ts = new ThreadedServer(int.Parse(args[0]));
    ts.Start();
    Console.ReadLine();
  }
}
This code expects you to run the application with the server port to bind to as the first command-line argument. Run the application specifying a port that isn't being used on your machine. While the application is running, open up a few command prompts and at each one use telnet to connect to your IP address and the port you are specifying when running the server. As soon as they've connected, any text you type into one command prompt window should immediately materialize in all of the others.

This threaded approach works quite well for a small server with a handful of clients, and it is very easy to code. Unfortunately, it does not scale well at all. The main drawback is the sheer number of threads that are created and destroyed. On my machine, the server can accept about 1000 simultaneous connections before getting "out of memory" exceptions (the machine quickly runs out of memory because each running thread has its own stack, which by default is a megabyte in size).

Using Select for Multiplexed I/O

There are several ways to avoid having to create a thread for each connection. The first is simply to use a thread pool—an approach that requires only minimal modifications to the previous example. This has the benefit of throttling the number of threads that can exist at any one time and is a viable approach if connections will be ephemeral. However, as is the case with a chat server, connections to servers typically remain open for prolonged periods of time, so limiting the number of connections to the number of threads in the pool is frequently not a practical approach.

A better solution is to use the static Socket.Select method. You can pass to Select three lists of Sockets you want to be monitored, one for readability, one for writability, and one for error conditions. When Select returns, items that remain in the lists are ready for the operation represented by that particular list. The method also accepts a microSeconds parameter that tells the method how long it should wait for a response. One way to use Select is to create a timer that fires frequently, and within the timer's event handler call Select with a timeout value of zero so that the call is non-blocking. However, this is inefficient. How long it takes to service the sockets would depend on how frequently you timer fires, resulting in a lot of thrashing for no good reason. To avoid this, it's typically better to create a thread that does nothing but call Select and service the requests.

Let's first look at how to use Select when accepting connections (see Figure 6). A listening socket should be put into the read list for the call to Select. If the listening socket is still present in the read list after the call to Select, there is a connection to be accepted (using Select in this fashion to determine that a request is available guarantees that Accept will not block). At this point, you can call Accept and deal with the accepted socket. Conveniently enough, the accepted sockets need to be placed in the same list as the Accept socket in order to service incoming data. This means that there is only one list to maintain for your little server.

Figure 6 Select-Based Server (Close Figure 6)
class SelectBasedServer
{
    ... // same SetupServerSocket and ctor as with ThreadedServer

    public void Start()
    {
        Thread selectThread = new Thread(ProcessSockets);
        selectThread.IsBackground = true;
        selectThread.Start();
    }

    private void ProcessSockets()
    {
        byte[] buffer = new byte[255];
        List<Socket> readSockets = new List<Socket>();
        List<Socket> connectedSockets = new List<Socket>();
        try
        {
            SetupServerSocket();
            while (true)
            {
                // Fill the read list
                readSockets.Clear();
                readSockets.Add(_serverSocket);
                readSockets.AddRange(connectedSockets);

                // Wait for something to do
                Socket.Select(readSockets, null, null, int.MaxValue);

                // Process each socket that has something to do
                foreach (Socket readSocket in readSockets)
                {
                    if (readSocket == _serverSocket)
                    {
                        // Accept and store the new client's socket
                        Socket newSocket = readSocket.Accept();
                        connectedSockets.Add(newSocket);
                    }
                    else
                    {
                        // Read and process the data as appropriate
                        int bytesRead = readSocket.Receive(buffer);
                        if (0 == bytesRead)
                        {
                            connectedSockets.Remove(readSocket);
                            readSocket.Close();
                        }
                        else
                        {
                            foreach (Socket connectedSocket in
                                connectedSockets)
                            {
                                if (connectedSocket != readSocket)
                                {
                                    connectedSocket.Send(buffer,
                                        bytesRead, SocketFlags.None);
                                }
                            }
                        }
                    }
                }
            }
        }
        catch (SocketException exc)
        {
            Console.WriteLine("Socket exception: " + exc.SocketErrorCode);
        }
        catch (Exception exc)
        {
            Console.WriteLine("Exception: " + exc);
        }
        finally
        {
            foreach (Socket s in connectedSockets) s.Close();
            connectedSockets.Clear();
        }
    }
}

Let's investigate the implications of using Select. Every time you call Select, you need to create lists of the sockets you want to monitor. When Select returns, these lists are modified to contain only sockets that require servicing. Although this sounds good, in practice it is quite inefficient. Imagine if 100 sockets had pending I/O operations at the same time: the 100th socket would be starved until you finished servicing or scheduling the first 99 sockets. The servicing of the sockets also prevents the code from reentering Select, which causes more potential for starvation of threads. You must ensure you have processed the socket I/O for all the sockets before you call Select again; failing to do so will result in getting notified for the same I/O more than once.

There are other performance drawbacks to using this method as well. You can see the Select performance degrade significantly after a thousand or so clients have connected. This is because the kernel has to internally poll every socket to determine whether there is data available.

Although you can connect significantly more sockets with this method than with the thread-per-request model, this is still not very scalable. You need to manage three different lists and iterate through each in order to service the request. This is more efficient from a thread usage standpoint, but it is much less responsive than using a thread-based server. There must be a better way to deal with multiple sockets.

Asynchronous I/O

The next step in scaling up the server is to use asynchronous I/O. Asynchronous I/O alleviates the need to create and manage threads. This leads to much simpler code and also is a more efficient I/O model. Asynchronous I/O utilizes callbacks to handle incoming data and connections, which means there are no lists to set up and scan and there is no need to create new worker threads to deal with the pending I/O.

So how does the .NET Framework deal with these asynchronous calls? Every .NET-based application has a thread pool associated with it. When an asynchronous I/O function has data that is ready to be processed, a thread from the .NET thread pool performs the callback function. When the callback is complete, the thread is released back into the thread pool. This is different from the approach discussed before where a thread pool thread was dedicated to a particular request; in this case, a thread from the pool is used only for a single I/O operation.

.NET uses a fairly simple model for asynchronous operations. All you need to do is call the relevant Begin method (BeginAccept, BeginSend, BeginReceive, and so on). with an appropriate callback delegate, and within your callback call the corresponding End method (EndAccept, EndSend, EndReceive, and so on) to get the results. All of the asynchronous Begin routines allow the user to pass in a context state object, which can be anything you want. When the asynchronous operation is complete, the object is part of the IAsyncResult that is passed into the callback.

In this new asynchronous server, we first call BeginAccept. With the first two servers presented in this article we were grappling with two different issues. The threaded server was reasonably fast, but could not accept many connections. The Select-based server did not suffer from the same connection limitations, but paid the price with a performance hit.

The asynchronous server removes the limitations on the number of connections it can accept (with some minor exceptions, as I'll explain in the scalability section), and it does so without the negative performance implications of the Select-based model. In fact, the performance can be better than in the threading model because the code does not bear the overhead associated with creating and tearing down threads.

Once the accept operation has completed, the next step is to queue an asynchronous read. This allows the server to read from the socket without having to explicitly poll or create threads. The asynchronous read is started with a call to BeginRead, which the results returned by a call to EndRead. The resulting server (see Figure 7) has significantly higher performance than the Select-based server and it can also handle far more connections than the thread-based server. This could be a good solution for the scalable server, but the server could still be made to work even better.

Figure 7 Asynchronous Server (Close Figure 7)
class AsynchronousIoServer
{
    ... // same SetupServerSocket and ctor as with ThreadedServer

    private class ConnectionInfo
    {
        public Socket Socket;
        public byte[] Buffer;
    }

    private List<ConnectionInfo> _connections =
        new List<ConnectionInfo>();

    public void Start()
    {
        SetupServerSocket();
        for (int i = 0; i < 10; i++)
            _serverSocket.BeginAccept(
                new AsyncCallback(AcceptCallback), _serverSocket);
    }

    private void AcceptCallback(IAsyncResult result)
    {
        ConnectionInfo connection = new ConnectionInfo();
        try
        {
            // Finish Accept
            Socket s = (Socket)result.AsyncState;
            connection.Socket = s.EndAccept(result);
            connection.Buffer = new byte[255];
            lock (_connections) _connections.Add(connection);

            // Start Receive and a new Accept
            connection.Socket.BeginReceive(connection.Buffer, 0,
                connection.Buffer.Length, SocketFlags.None,
                new AsyncCallback(ReceiveCallback), connection);
            _serverSocket.BeginAccept(new AsyncCallback(AcceptCallback),
                result.AsyncState);
        }
        catch (SocketException exc)
        {
            CloseConnection(connection);
            Console.WriteLine("Socket exception: " + exc.SocketErrorCode);
        }
        catch (Exception exc)
        {
            CloseConnection(connection);
            Console.WriteLine("Exception: " + exc);
        }
    }

    private void ReceiveCallback(IAsyncResult result)
    {
        ConnectionInfo connection = (ConnectionInfo)result.AsyncState;
        try
        {
            int bytesRead = connection.Socket.EndReceive(result);
            if (0 != bytesRead)
            {
                lock (_connections)
                {
                    foreach (ConnectionInfo conn in _connections)
                    {
                        if (connection != conn)
                        {
                            conn.Socket.Send(connection.Buffer, bytesRead,
                                SocketFlags.None);
                        }
                    }
                }
                connection.Socket.BeginReceive(connection.Buffer, 0,
                    connection.Buffer.Length, SocketFlags.None,
                    new AsyncCallback(ReceiveCallback), connection);
            }
            else CloseConnection(connection);
        }
        catch (SocketException exc)
        {
            CloseConnection(connection);
            Console.WriteLine("Socket exception: " + exc.SocketErrorCode);
        }
        catch (Exception exc)
        {
            CloseConnection(connection);
            Console.WriteLine("Exception: " + exc);
        }
    }

    private void CloseConnection(ConnectionInfo ci)
    {
        ci.Socket.Close();
        lock (_connections) _connections.Remove(ci);
    }
}

What About Scalability?

No matter which of the servers models I use, on my machine I can't top approximately 4000 simultaneous connections. At that point new client requests start to fail with error messages about lack of buffer space or about queues that are too full: not particularly helpful messages. Obviously this is an unacceptable limit if you want a server to do any heavy-duty processing. So why is the code hitting this wall? It turns out that the limiting factor is the machine's available memory resources, specifically the nonpaged memory pool. When the process has exhausted its nonpaged memory pool, no more socket connections can be made. [ Editor's Update - 1/3/2006: The ephemeral port range is also a limiting factor. For more information, see When you try to connect from TCP ports greater than 5000 you receive the error 'WSAENOBUFS (10055)'.]

Socket Client Applications

So now that you've written a server that is very responsive and scalable, you need to make the client application efficient as well. Although the code to do so is relatively simple, there are several considerations when writing a socket client.

As with all socket applications, the client socket must first be created. Once created, it may or may not be bound explicitly to an address and port. If you choose not to bind your client socket to an address, it will be given whatever address the network layer determines is best. If a port is not selected, the network layer will provide a unique port within the range 1024 to 5000.

Why then would you want to bind a client socket to an address or port? Consider the situation in which you have a multi-homed client machine and you want to choose the NIC that your client will use. You should bind the client socket to the IP address of the desired NIC. Fortunately, you can call Bind with a port number of 0 and the network layer will still provide you with a unique port within the range of 1024 to 5000. Binding a client socket to a specific port is not recommended, but it is possible.

Once the socket is created, and possibly bound, it is time to connect to the server. Just as with most of the server calls, the connection to the server can be made synchronously or asynchronously. The asynchronous version of Connect is useful if your client needs to establish connections to multiple servers. Instead of having each connection wait for the previous one to complete, the application can start multiple connections at the same time.

There is a very real possibility of getting a WSAECONNREFUSED error when attempting to connect a client socket, even if the server is available. This can happen if the server's listen queue is full. For this reason, a client may want to handle this exception by periodically retrying its connection.

What's Next?

Now that you've determined that the asynchronous socket model is the most scalable, how do you continue to improve performance? Let's break down the functions that a server needs to perform, and see where to make improvements.

The first thing a server needs to do is accept connections. There are three basic ways to do an asynchronous accept: just accept the connection, accept the connection and receive the first bytes of data on that connection, and accept the connection using a specific socket you provide, receiving the first bytes of data on the connection. Only the first of these approaches is available in the .NET Framework 1.x. In the .NET Framework 2.0, all three are available as overloads of BeginAccept and EndAccept.

The code for the servers in this article has been based on the basic asynchronous accept. The advantage of just accepting the connection is that you can separate your accept logic from your message-processing logic. This is a very simple programming model, but it may not provide the best performance.

The advantage of the "accept and receive" approach is a performance boost on your first message, primarily because you have to make fewer expensive calls to the kernel. This method works quite well if you have a known first few bytes, like a protocol header. The down side of this approach is that the callback supplied to BeginAccept is not called until the receive is complete. This may lead to connected sockets that your application does not know about.

What about the "accept and receive" method where you can pass in a socket? This offers a great advantage in the .NET world, especially when combined with the .NET Framework 2.0 Socket.Disconnect call. Historically, when you were finished using the socket, you needed to close the connection and delete the socket object. With Socket.Disconnect, you can reuse Socket instances, which provides you with the means to implement your own Socket object pool. In certain scenarios having this kind of control can result in a long-term performance boost.

Now let's think about how socket connection processing works. First, you tell the operating system to listen for connections, and it creates a queue to hold a certain number of pending connections. Let's assume the maximum size is 200. This means that if more than 200 clients try to connect simultaneously, you will get WSAECONNREFUSED errors (the server actively refused the connection) in each of the clients that overflowed the listen queue. A remedy for this is to queue multiple accepts; the multiple accepts will be able to pull pending connections from the queue even if you are still doing your processing and have not resubmitted the accept request. This effectively increases the depth of your listening queue. If you take another look at Figure 7, you'll see that I start with 10 asynchronous calls to accept.

However, queuing multiple asynchronous operations has a cost. Earlier I discussed why I was getting a limited number of connections on what should be a highly scalable server. These asynchronous calls deplete the same resource pool that was causing the connection problem; in this case, greater performance comes at the cost of scalability.

Debugging a Sockets Application

One new feature in .NET Framework 2.0 is the ability to perform robust network tracing. This feature can make debugging socket applications much easier than in the past, when you typically relied solely on a debugger and a packet sniffer. By setting up some parameters within your configuration file, you can see critical events, errors, warnings, method entry and exits, and actual network traffic. This is all exposed via the standard Trace functionality within .NET.

In the System.Diagnostics section of the configuration file, you need to add a trace source for the System.Net namespace you want to log (valid sources include "System.Net", "System.Net.Sockets", and "System.Net.Cache"). In this case, that would be System.Net.Sockets. An example of the application configuration file is shown in Figure 8.

Figure 8 Configuration File (Close Figure 8)
<configuration>
  <system.diagnostics>
    <sources>
      <source name="System.Net.Sockets">
        <listeners>
          <add name="Sockets"/>
        </listeners>
      </source>
    </sources>
    <switches>
      <add name="System.Net.Sockets" value="31" />
    </switches>
    <sharedListeners>
    <add name="Sockets" type="System.Diagnostics.TextWriterTraceListener"
      initializeData="Sockets.log"/>
    </sharedListeners>
    <trace autoflush="true" />
  </system.diagnostics>
</configuration>

Once the network tracing has been enabled, you will start to see a lot of internal information about your sockets-based applications. The value attribute for the System.Net-based trace logging is a bit mask with each bit representing a different level of logging. There are five levels you can enable: network traffic (0x10), method entry and exit (0x8), warnings (0x4), errors (0x2), and critical events (0x1). The example in Figure 8 is logging everything, for a combined value of 31.

Tracing method entry and exit is a very useful tool as it details when you are calling different methods within the System.Net namespace. This feature not only helps you to debug your own program, but it can give some insight as to how the API is working internally. For calls that have a return value, the return is shown in the exiting log for that call. Following is an example of the method entry and exit log for an asynchronous accept call; notice that from this log you can tell that a socket was created within the API. The return value from BeginAccept is an AcceptOverlappedAsyncResult, and the object ID for this particular call is 48209832.

System.Net.Sockets Information: 0 : Socket#48285313::BeginAccept()
System.Net.Sockets Information: 0 : Socket#59817589::Socket(InterNetwork#2)
System.Net.Sockets Information: 0 : Exiting Socket#59817589::Socket()
System.Net.Sockets Information: 0 : Exiting Socket#48285313::BeginAccept()
-> AcceptOverlappedAsyncResult#48209832

You should keep in mind that not all methods provide the network trace hooks. For instance, Select does not show up in the trace logs, but Bind does. Check the documentation to determine which methods support the trace options (see Network Tracing).

The network traffic log is another very useful tool. This log is very straightforward; it is simply a dump of the data buffer from the send or the receive. One thing to be aware of is that your buffers will be dumped in their entirety (up to the maximum specified size), which may be misleading. You may only receive 1 byte in a 512-byte receive buffer, but you will see the contents of the entire buffer. You have to be certain to check the return value of the send or the receive to ensure the bytes you are looking at are relevant. The following trace is from an application sending out 1 byte of data (an "a") on socket 60504909.

System.Net.Sockets Information: 0 : Socket#60504909::Send()
System.Net.Sockets Verbose: 0 : 00000000 : 61 : a
System.Net.Sockets Information: 0 : Exiting Socket#60504909::Send()
-> 1#1

The other three levels of logging (warnings, errors, and critical events) provide details about failures within the APIs. Each of these logging types is preceded in the log by their level and contains details about the message. For instance, if a call to Socket.GetSocketOptions fails, an error is generated and appears in the log as you can see here:

System.Net.Sockets Error: 0 : Exception in the
Socket#48285313::GetSocketOption - An unknown, invalid, or unsupported
option or level was specified in a getsockopt or setsockopt call

The new logging capabilities of System.Net are very powerful and can be extremely useful in debugging your sockets application. The only thing to be wary of when using this debugging facility is the sheer quantity of data it can potentially generate. It can quickly fill a disk with debugging information.

As you can see, there are multiple approaches to improving the performance of socket applications. This article only scratched the surface of the full socket API.

Daryn Kiely works in the research and development department at International Game Technology in Las Vegas, Nevada. He can be contacted at thekielys@earthlink.net.


Easy UI Testing: Isolate Your UI Code Before It Invades Your Business Layer

Easy UI Testing

Isolate Your UI Code Before It Invades Your Business Layer

Mark Seemann
This article discusses:
  • The User Interface Process Application Block, version 2
  • The Model View Controller pattern
  • Unit-testing user interface processes
  • Using and testing user interface process events
This article uses the following technologies:
C#, NUnit
Code download available at: UIPApplicationBlock.exe (260KB)
Contents
Thick User Interfaces
Implementing Model View Controller
A Trip Around the Solar System
Creating the Controller
Preparing a Unit Test Project
Unit-Testing the Controller
Testing Inside the UI Flow
Raising and Testing Events
Thin User Interfaces
Conclusion

M
any applications today have complex user interface requirements with many different screens and complex logic dictating their interaction. Most developers know that they should design their apps in layers, but my experience has shown me that in reality it is much more difficult than anticipated to isolate the user interface from the rest of the application.

The User Interface Process Application Block (UIP) from the Microsoft Patterns & Practices team can help you isolate your UI, but why stop there? With UIP you can now write unit tests of all (or most) of your UI logic, and the UI finally becomes the thin layer on top of the app that it was always meant to be.

Using a simple example, I will show you how to use UIP to create a multiscreen application and how to write unit tests for the user interface logic. The example starts out as a Windows® Forms application, but the actual UI contains so little code that it subsequently becomes very easy to create a second, ASP.NET user interface for the application. I'll also briefly discuss how this works.

Thick User Interfaces

Ideally, user interfaces should only be thin shells on top of the next layer in an application. UI should really only do two things: display information in a useful fashion, and pass user input as quickly as possible to some code that knows what to do with it. In reality, however, user interface code usually includes a lot of logic which determines where to go next, how to communicate user selections to the next screen, where to get data, and so on. This is particularly prevalent in Web applications, where there's no obvious, object-oriented way to pass data between two Web pages. Instead, Web applications typically include lots of code which transforms page variables into form fields or query strings, as well as code which reads, validates, and transforms these fields and query strings to input. Typically, you will see liberal use of code like this:

string searchPhrase = Request.QueryString["SearchPhrase"];
searchPhrase = Server.UrlDecode(searchPhrase);

int page = 1;
try { page =     int.Parse(Request.QueryString["SearchPage"]); }
catch{}

// Use searchPhrase and page in further code
This sort of code is bad for several reasons. You can probably see a lot of the problems yourself, so I'll focus on the main ones: maintenance and testability.

Such code relates to an application's infrastructure and has nothing to do with the actual task at hand. Each page probably has its own distinct set of input variables it needs to handle, so it's difficult to extract this infrastructure code into a common library. The code gets harder to read, and when you need to modify a form field, you need to modify all the pages that handle the field. The application quickly becomes hard to maintain and extend.

It is typically more difficult to perform automated tests on code residing in the user interface, and the more logic contained there, the greater the need for automated testing. With Web applications, automated user interface test tools exist, but such tests are typically more brittle than unit tests. With Windows-based applications, such test tools are less prevalent and, although managed Windows applications don't have the same need for infrastructure code as Web applications, user interface logic can quickly become bloated just the same.

Implementing Model View Controller

As with most problems in object-oriented development, the solution is to add a new level of indirection. The key solution is to insert a new controlling layer between the user interface and the domain model. There's a basic design pattern called Model View Controller which describes this approach.

Instead of letting a Web page or Windows form communicate directly with the domain model, a controller manages the communication between the two other layers. In good, layered fashion, the domain model is completely unaware of both controller and view. The controller uses the domain model, but is unaware of the view and the view uses the controller but is unaware of the domain model. Layers are unaware of their consumers, but may raise events to notify them of changes.

Instead of implementing Model View Controller yourself, you can use the UIP Application Block. This reusable code module provides a sophisticated implementation of Model View Controller and is available at User Interface Process (UIP) Application Block.

With UIP, you can create controllers for many different scenarios. In the example I'm about to show you, I use UIP in a fashion that closely follows the Application Controller design pattern, as described in Martin Fowler's Patterns of Enterprise Application Architecture (Addison-Wesley, 2002).

A Trip Around the Solar System
Figure 1 Booking Application UI Flow
Figure 1 Booking Application UI Flow

In this age of emerging space tourism, let's imagine a travel company selling tickets to various destinations in our solar system. They need a booking application for their salespeople to record customer travel requests. As an example of how to use UIP, I'll show you how to create and unit-test a simple booking application. The workflow of the booking application is shown in Figure 1. In its initial state, the application is just waiting for a new booking process to begin. When it does, customer information is recorded as the first step. Then a travel destination is selected. As the third step, a summary screen is shown with the trip details so far. From the trip details screen, it is possible to add more travel destinations, book the trip, or cancel the whole operation. While looking at the sample application, I will focus on the controller, since creating domain logic and user interfaces should be familiar to most of you.

Creating the Controller

The Controller is not your business logic layer, but rather it is a thin adapter over the business logic layer which basically intercepts calls from the user interface in order to implement abstraction and state management. You may as well know up front that creating and configuring the controller is a bit complicated, but it's well worth the effort. The first thing to do is to create a controller class and set up the application's configuration file. Creating the controller is as easy as the following:

public class BookingController : ControllerBase
{
    public BookingController(Navigator context) : base(context) {}
}

BookingController inherits from Microsoft.ApplicationBlocks.UIProcess.ControllerBase and implements a single constructor, which takes a Navigator object as the only parameter. In UIP, the Navigator object manages the control flow of the user interface process. Depending on your needs, different navigators are available in UIP to direct the interaction of views. In the booking sample application, there are well-defined transitions between each view, so I'll use a navigator object called a Graph Navigator.

The Graph Navigator uses the application's configuration file to determine which views are available and which transitions are legal (the navigation graph reduces the user interaction workflow to an explicit finite state machine encoded in the configuration XML). Figure 2 shows the navigationGraph section of the UIP configuration. Notice how each view and transition in Figure 1 is defined in the configuration. Together with other configuration data, UIP uses this information to control transitions between views.

Figure 2 navigationGraph of UIP (Close Figure 2)
<navigationGraph iViewManager="WindowsFormViewManager"
        name="BookingManager" state="State"
        statePersist="MemoryPersistState" startView="Start">
    <node view="Start">
        <navigateTo navigateValue="createNewTrip"
            view="PassengerInformation" />
    </node>
    <node view="PassengerInformation">
        <navigateTo navigateValue="addLegToTrip"
            view="SelectDestination" />
    </node>
    <node view="SelectDestination">
        <navigateTo navigateValue="confirmSelection" view="TripDetails"
        />
    </node>
    <node view="TripDetails">
        <navigateTo navigateValue="addLegToTrip"
            view="SelectDestination" />
        <navigateTo navigateValue="bookTrip" view="TripDetails" />
        <navigateTo navigateValue="cancel" view="Start" />
    </node>
</navigationGraph>

As a controller manages communication between views, it needs some way to communicate the state between one view and the next. UIP does this by containing a state object in each controller. You can use the default State class, which is basically a serializable dictionary object with a few more properties, but to make development a little easier, I usually prefer to create a strongly typed state class instead. Notice that you can achieve the same functionality with the default State class, so the main purpose of a strongly typed state class is to enhance code readability and maintainability.

Creating a strongly typed state class for UIP isn't difficult at all, and it's described well in the documentation, so I won't go into details. Just think about the state class as a strongly typed property bag with the following public API:

public class BookingState : State {
    public event StateChangedEventHandler StateChanged;
    public void Reset();
    public string FirstName { get; set; }
    public bool IsBooked { get; set; }
    public string LastName { get; set; }
    public StringCollection SelectedDestinations { get; }
    public TravelClass TravelClass { get; set; }
    ...
}
If you are interested in learning more about how the BookingState class is implemented, it's available in the download that accompanies this article, available at the MSDN®Magazine Web site.

You need to instruct UIP to use the BookingState and BookingController classes. You do this in the application configuration file:

<objectTypes>
    ...
    <state name="State" type="BookingUip.BookingState, BookingUip,
        Version=null, Culture=neutral, PublicKeyToken=null" />
    <controller name="BookingController"
        type="BookingUip.BookingController, BookingUip, Version=null,
        Culture=neutral, PublicKeyToken=null" />
    ...
</objectTypes>
This code causes UIP to populate BookingController's State property with an instance of the BookingState class. Although I could access the property and cast it to BookingState, this doesn't make my code any more readable, so for convenience I've created a property on the BookingController class which makes it easier for me to access my strongly typed state class:
public BookingState BookingState
{
    get { return (BookingState)this.State; }
}

Finally, I'm ready to begin creating the real logic of BookingController. To start, I'll create a method to begin a new booking process. This corresponds to making the first, "Create New Trip" transition in Figure 1:

public void CreateNewTrip()
{
    this.BookingState.Reset();
    this.BookingState.Save();
    this.Navigate("createNewTrip");
}
As you can see, it's pretty simple stuff. Since it's a new booking process, it's always a good idea to reset the internal state. After modifying the state in UIP, you should always call the Save method. For this application, I'm using an in-memory state. If I decide to change to a persistable state, it's necessary to call the Save method to persist the new state to the underlying storage.

The last line of code instructs UIP to perform a navigation action. Note that I'm not telling it which view it should navigate to, but rather which transition to perform. If you take a look at Figure 2 again, you will see that the createNewTrip transition indeed takes me from the Start view to the PassengerInformation view.

Preparing a Unit Test Project

Obviously I'll need to add more methods to the controller, but before doing that, I want to create a unit test for the CreateNewTrip method. Like creating a controller, it takes a bit of effort to set up the unit test project. Currently, I'm using NUnit 2.2 (available at www.nunit.org) for unit testing, and I always create a separate project for unit tests. After adding the appropriate references to the new project, you need to create a configuration file to hold all the UIP configuration entries.

Since the unit test project is a library project, Visual Studio® .NET isn't going to copy the App.config file from the project folder to the output folder or rename it properly. For that reason, it's necessary to instruct Visual Studio to do so manually. This can be done by adding the following post-build event to the unit test project:

copy "$(ProjectDir)App.config" "$(TargetDir)$(TargetFileName).config"
This will copy and rename the App.config file to the output folder. My unit test project is called BookingUipUnitTests, so in debug mode the file will by copied to \bin\debug\BookingUipUnitTests.dll.config. NUnit will then automatically pick up and use the configuration file just like any other app config file.

Although it's possible to reuse most of the configuration settings from the real application that's being developed, it's necessary to modify a few settings to make it work under unit test conditions. Since the whole idea of unit testing is to test an isolated library with as few dependencies as possible, it's necessary to remove any references to the UI project from the configuration file.

The UIP section of the configuration file has a section where you define how each view is implemented. In the real presentation layer application, this section is used to specify which Windows Forms or Web pages correspond to which views. UIP then uses this information to activate and show the correct view based on the configured navigation graph.Stub View and View Manager

In my article, I create a test view as a Windows Form which doesn't really do anything, as a stub for testing. While this method works well, it may not be the best way. A better approach would be to use a dedicated test stub view.

In UIP, views are classes that implement the IView interface. The application block comes with three view classes (WebFormView, WindowsFormView, and WindowsFormControlView), but new implementations can be created. Views are managed by corresponding view managers, which are classes implementing the IViewManager interface.

For unit-testing purposes, a stub view and corresponding view manager can be created. The purpose of creating these stubs would be to do as little as possible, but still provide stubs for UIP to invoke when necessary. As such, the stub view manager, shown in Figure A, is simple.

As you can tell, StubViewManager doesn't do much besides implement the IViewManager interface. Creating a stub view is even simpler, as shown in Figure B.

When using the stub view and view manager classes for unit testing, the configuration file for the testing project should be modified to reflect the changes. Instead of the WindowsFormViewManager used in the article, the StubViewManager class is used, as shown here:

   <iViewManager name="StubViewManager"
       type="BookingUipUnitTests.StubViewManager,
       BookingUipUnitTests, Version=null, Culture=neutral,
       PublicKeyToken=null" />

Correspondingly, the views should now be defined with the StubView class, as shown in Figure C, instead of the TestForm class used in the article.

Because the stub view manager never activates the stub views, execution time is better than with the test form, which is still being loaded and initialized during each test. An informal test where I ran each configuration ten times indicates that using the stub views and view manager result in 30 percent better performance, so if execution time during unit testing is an issue, this approach can be used to speed things up a bit.

Figure C Configuring Views as Stub Views (Close Figure C)
<views>
    <view name="Start" type="BookingUipUnitTests.StubView,
        BookingUipUnitTests, Version=null, Culture=neutral,
        PublicKeyToken=null" controller="BookingController" />
    <view name="PassengerInformation"
        type="BookingUipUnitTests.StubView,
        BookingUipUnitTests, Version=null, Culture=neutral,
        PublicKeyToken=null" controller="BookingController" />
    <view name="SelectDestination" type="BookingUipUnitTests.StubView,
        BookingUipUnitTests, Version=null, Culture=neutral,
        PublicKeyToken=null" controller="BookingController" />
    <view name="TripDetails" type="BookingUipUnitTests.StubView,
        BookingUipUnitTests, Version=null, Culture=neutral,
        PublicKeyToken=null" controller="BookingController" />
</views>
Figure B Creating a Stub View (Close Figure B)
public class StubView : IView
{
    public void Enable(bool enabled) {}
    public ControllerBase Controller { get { return null; } }
    public string ViewName { get { return null; } }
    public Navigator Navigator { get { return null; } }
    public Guid TaskId { get { return Guid.Empty; } }
    public void Initialize(
        TaskArgumentsHolder args, ViewSettings settings) {}
}
Figure A Stub View Manager (Close Figure A)
public class StubViewManager : IViewManager
{
    private Hashtable tasks_;
    private readonly static object placeHolder_ = new object();

    public StubViewManager() { this.tasks_ = new Hashtable(); }

    // Not necessary for all ViewManagers
    public int GetActiveViewCount() { return 0; }

    // Not necessary for all ViewManagers
    public void StoreProperty(Guid taskId, string name, object value)
       {}

    // Required for web applications only
    public bool IsRequestCurrentView(IView view, string stateViewName)
    {
        return true;
    }

    public void ActivateView(string previousView, string view,
        Navigator context, TaskArgumentsHolder args)
    {
        Guid taskId = context.CurrentState.TaskId;
        this.tasks_[taskId] = StubViewManager.placeHolder_;
    }

    void Microsoft.ApplicationBlocks.UIProcess.IViewManager.
        ActivateView(string previousView,
        string view, Navigator context)
    {
        this.ActivateView(previousView, view, context, null);
    }

    public Guid[] GetCurrentTasks()
    {
        Guid[] returnValue = new Guid[this.tasks_.Keys.Count];
        this.tasks_.Keys.CopyTo(returnValue, 0);
        return returnValue;
    }

    // Required for web applications only
    public string GetViewNameForCurrentRequest(IView currentView)
    {
        return null;
    }
}

In the real application, I've defined a Windows Form class for each view, but I don't want to have a dependency on these classes in my unit test. Additionally, in unit testing I can't have a reliance on any UI, as I may want to run my unit tests as a scheduled job. As always, the solution is to create a stub that can be activated instead of the real UI.

To create a stub, I add a new Windows Form called TestForm to my unit test project. This form contains absolutely no functionality; the only change I make is setting its WindowState property to Minimized so I don't have to watch it flicker on and off when I execute the tests.

With this form, I can now define the views section of the configuration file to use only the stub form. As you can see in Figure 3, all four views are defined to use the same TestForm class. Now, when the tests execute, UIP will read this configuration file and each time it needs to activate a view, it will create, show, and dispose an instance of TestForm, but it will happen so quickly that you'll hardly notice it. If you find that this approach is a bit of a hack, see the sidebar on how to implement a test stub view.

Figure 3 Defining the Views (Close Figure 3)
<views>
    <view name="Start" type="BookingUipUnitTests.TestForm,
        BookingUipUnitTests, Version=null, Culture=neutral,
        PublicKeyToken=null" controller="BookingController" />
    <view name="PassengerInformation" type="BookingUipUnitTests.TestForm,
        BookingUipUnitTests, Version=null, Culture=neutral,
        PublicKeyToken=null" controller="BookingController" />
    <view name="SelectDestination" type="BookingUipUnitTests.TestForm,
        BookingUipUnitTests, Version=null, Culture=neutral,
        PublicKeyToken=null" controller="BookingController" />
    <view name="TripDetails" type="BookingUipUnitTests.TestForm,
        BookingUipUnitTests, Version=null, Culture=neutral,
        PublicKeyToken=null" controller="BookingController" />
</views>

Unit-Testing the Controller

So far I've only set up and configured the unit test project, but I haven't written any test code. With the entire configuration in place, I can finally create a skeleton class to hold my unit tests, as illustrated in Figure 4. At this point, I've only created an initialization method and a few helper properties.

Figure 4 Unit Test Class (Close Figure 4)
[TestFixture]
public class BookingControllerTests
{
    private BookingController controller_;

    [SetUp]
    public void CreateController()
    {
        GraphNavigator gn = new GraphNavigator("BookingManager");
        this.controller_ = new BookingController(gn);
    }

    private GraphNavigator Navigator
    {
        get { return (GraphNavigator)this.controller_.Navigator; }
    }

    private BookingState State
    {
        get { return (BookingState)this.controller_.State; }
    }
}

The CreateController method ensures that I always have an initialized BookingController object available. The controller object is initialized with the graph navigator specified in Figure 2, as the name of the graph is supplied to the GraphNavigator constructor. The two private properties are just helpers to make the rest of my test code a bit more readable.

To test the CreateNewTrip method, I write the test method in Figure 5. The first thing I need to do is initialize the associated Navigator object in some way. This can be done in more than one way, but since it's the initiation of a new booking process that I'm testing, I can call the StartTask method on the GraphNavigator. This instructs UIP to begin a new navigation task as defined in the configuration file, and since the configured navigation graph specifies that the start view is the view called Start, this will be the initial view after calling the StartTask method.

Figure 5 Testing CreateNewTrip (Close Figure 5)
[Test]
public void InitiateNewTripCreation()
{
    this.Navigator.StartTask();
    this.controller_.CreateNewTrip();

    StringCollection selectedDestinations =
        this.controller_.GetSelectedDestinations();
    Assert.AreEqual(0, selectedDestinations.Count);

    bool isBooked = this.controller_.IsTripBooked;
    Assert.AreEqual(false, isBooked);
    string newViewName = this.Navigator.CurrentState.CurrentView;
    Assert.AreEqual("PassengerInformation", newViewName);
}

Next I invoke the CreateNewTrip method and validate that the controller has the expected values. The last assertion in the test verifies that after calling CreateNewTrip, the controller has transitioned to the PassengerInformation view, as is also specified in the configuration file. This is basically all it takes to unit-test a method on a controller.

Testing Inside the UI Flow

The first test I showed you demonstrated how to test the controller at the beginning of a navigation task. Things work a little differently when you want to test how the controller behaves in the middle of a UI flow, such as when the user adds a destination to the list of destinations from the Select Destination view.

The controller's method to add a destination is shown here:

public void ConfirmDestinationSelection(string selectedDestination)
{
    this.BookingState.SelectedDestinations.Add(selectedDestination);
    this.BookingState.Save();
    this.Navigate("confirmSelection");
}

It simply adds the new destination to the internal list of destinations, saves the state, and instructs UIP to perform the confirmSelection view transition. To test this method, I write the unit test in Figure 6. Before invoking the ConfirmDestinationSelection method, I need to put the controller in a state corresponding to the Select Destination view. To do so, I need to tell UIP which view is the current view, and I need to initialize relevant parts of the internal state object.

Figure 6 ConfirmDestinationSelection Method (Close Figure 6)
[Test]
public void AddDestination()
{
    this.Navigator.CurrentState.CurrentView = "SelectDestination";
    this.State.SelectedDestinations.Add("Earth");

    this.controller_.ConfirmDestinationSelection("The Moon");

    StringCollection selectedDestinations =
        this.controller_.GetSelectedDestinations();

    Assert.AreEqual(2, selectedDestinations.Count);
    Assert.AreEqual("Earth", selectedDestinations[0]);
    Assert.AreEqual("The Moon", selectedDestinations[1]);

    string newViewName = this.Navigator.CurrentState.CurrentView;
    Assert.AreEqual("TripDetails", newViewName);
}

According to my test scenario I'm now in the middle of the booking process, so I can't initialize the GraphNavigator by calling the StartTask method, as that would begin a new booking process with the Start view. Luckily I can access the Navigator's state, and directly manipulate it by telling it the name of the current view.

Although I don't really have to, I decide that I want to test a scenario in which, in the initial state, the Earth destination has already been added, as this allows me to test that a new destination isn't going to override any destinations that were already selected.

After setting up the initial state of my test, I invoke the ConfirmDestinationSelection method on the controller, which should cause two things to happen. The new destination should have been added to the internal list of destinations, and the view should have changed to the TripDetails view. If these conditions are true, no assertions will fail, and the test will pass.

Raising and Testing Events

According to good object-oriented design, a component should not know anything about its client, but should instead raise events whenever something important happens. In UIP, the controller's State object raises the StateChanged event whenever something happens to change the state. The BookingState class, which inherits from State, implements the same behavior, because every time a property is set, the StateChanged event is raised. Generally, the booking app is not going to care about these events, so it will just ignore them, but for the Trip Details view, I'd like to know if the state changed as a result of a booking, so controls in the view can be updated. Booking a trip is done by the BookTrip method:

public void BookTrip()
{
    /* Should really write complete trip information to
     * domain model layer, but this is omitted for clarity. */
    this.BookingState.IsBooked = true;
    this.BookingState.Save();
    this.Navigate("bookTrip");
}

Nothing much is actually happening here, but since I set the IsBooked property, the StateChanged event ought to be raised. To test this, I write the unit test in Figure 7. Before invoking the BookTrip method, I set the eventWasRaised_ member variable to false and subscribe to the StateChanged event. When BookTrip is called, this ought to invoke the event handler, where I validate that the event data is as expected. If everything checks out, I set the eventWasRaised_ member variable to true. When the event handler is done executing, control returns to the test method where further checks are performed to validate that the event was, in fact, raised, and that the IsTripBooked property is now true.

Figure 7 Testing the BookTrip Method (Close Figure 7)
// Member variable...
private bool eventWasRaised_;

[Test]
public void BookTrip()
{
    this.Navigator.CurrentState.CurrentView = "TripDetails";

    this.eventWasRaised_ = false;
    this.State.StateChanged +=
        new BookingUip.BookingState.StateChangedEventHandler
        (BookingControllerTests_StateChanged);

    this.controller_.BookTrip();

    bool booked = this.controller_.IsTripBooked;
    Assert.AreEqual(true, booked);
    Assert.AreEqual(true, this.eventWasRaised_);
    string newViewName = this.Navigator.CurrentState.CurrentView;
    Assert.AreEqual("TripDetails", newViewName);
}

private void BookingControllerTests_StateChanged(object sender,
    StateChangedEventArgs e)
{
    Assert.AreEqual("IsBooked", e.Key);
    Assert.AreEqual(true, this.State.IsBooked);
    this.eventWasRaised_ = true;
}

Thin User Interfaces

I'll spare you the details of how to implement and unit-test the rest of the BookingController, but if you're interested, the complete controller is available in the sample code download.

Creating the UI for the booking application now becomes quite easy, since most code in the UI just delegates the work to the BookingController. A view in UIP should implement the IView interface, but fortunately, the application block comes with implementations of IView for Windows Forms and Web applications. To create the Start view, for example, I just need to inherit from WindowsFormView:

public class StartForm : WindowsFormView
   { /* Implementation... */ }
Since WindowsFormView inherits from the Form class that you'd usually subclass, you're still creating a Windows Form application.

The Start view only contains a single button which, when pressed, will start the booking process, so implementing the Start form is simple. The WindowsFormView class that StartForm is inheriting from contains a Controller property to which the real work can be delegated. In StartForm, I can implement the event handler of its single button like this:

private void createNewTripButton__Click(
    object sender, System.EventArgs e)
{
    ((BookingController)this.Controller).CreateNewTrip();
}
Clicking the button will now delegate the real work to the CreateNewTrip method, which will perform its work and subsequently let UIP manage the transition to a new view, as defined in the method. Since this example may seem laughably simple, let's look at the most complex view in the entire application, the Trip Details view, shown in Figure 8. This view should be able to load existing data, handle three different buttons, and enable and disable some of these buttons according to state. The implementation of this view is shown in Figure 9.

Figure 9 Implementation of TripDetailsForm (Close Figure 9)
public class TripDetailsForm : WindowsFormView
{
    // Auto-generated code goes here...

    private void TripDetailsForm_Load(object sender, System.EventArgs e)
    {
        this.firstNameLabel_.Text =
            this.BookingController.PassengerFirstName;
        this.lastNameLabel_.Text =
            this.BookingController.PassengerLastName;
        this.classLabel_.Text =
            this.BookingController.TravelClass.ToString();
        this.destinationsListBox_.DataSource =
            this.BookingController.GetSelectedDestinations();
        this.BookingController.BookingState.StateChanged +=
            new BookingUip.BookingState.StateChangedEventHandler(
                BookingState_StateChanged);
        this.SetButtonState();
    }

    private void addLegToTripButton__Click(
        object sender, System.EventArgs e)
    {
        this.BookingController.SelectDestination();
    }

    private void bookButton__Click(object sender, System.EventArgs e)
    {
        this.BookingController.BookTrip();
    }

    private void cancelButton__Click(object sender, System.EventArgs e)
    {
        this.BookingController.Cancel();
    }

    private void BookingState_StateChanged
        (object sender, StateChangedEventArgs e)
    {
        if(e.Key == "IsBooked") this.SetButtonState();
    }

    private void SetButtonState()
    {
        bool disabled = this.BookingController.IsTripBooked;
        this.bookButton_.Enabled = !disabled;
        this.addLegToTripButton_.Enabled = !disabled;
    }

    private BookingController BookingController
    {
        get { return (BookingController)this.Controller; }
    }
}
Figure 8 The Trip Details Form
Figure 8 The Trip Details Form

Loading the existing data and handling the button click events is trivial, since it just delegates all logic to the BookingController. Managing the state of the buttons is a bit more complex, but still easy to implement. All three buttons should be enabled as long as the trip has not been booked. When the trip is booked, the Add Leg to Trip and Book buttons should be disabled, as the trip can't be edited once it's booked. For that reason, I add an event handler to the StateChanged event so that the view will always be able to react to changes in the underlying state of the controller. If the event is raised, the code investigates whether the event is related to the IsBooked property, and if that is the case, calls the SetButtonState method, which enables or disables the buttons as required.

Conclusion

Using UIP provides several benefits. It pulls the complex part of the user interface code into a library that can be unit-tested, it abstracts away the need to think about communication between views, and it makes it very easy to create different view implementations for the same application. I began the creation of the sample booking program as a Windows Forms application, but when it was complete, I recreated it as an ASP.NET application, and it turned out to be basically the same, small amount of code I had to write.

It took me a couple of hours to create the ASP.NET application, and since UIP abstracts away all communication between views, there are no query strings or hidden form fields involved between the pages. Although UIP may at first seem like a complex and daunting piece of technology, it takes less than a day to learn and get started, and I'm sure that you will agree it is well worth the initial investment in time and effort.

For more information on topics discussed in this article, you should visit NUnit at www.nunit.org and take a look at User Interface Process Application Block, version 2 at User Interface Process (UIP) Application Block.

Mark Seemann works for Microsoft Consulting Services in Copenhagen, Denmark, where he helps Microsoft customers and partners architect, design, and develop enterprise-level applications. Mark can be reached at mseemann@microsoft.com.


Editor's Note: Connected Systems Developer Competition

Editor's Note
Connected Systems Developer Competition

Last month, we told you about a contest to help design the next-generation PC hardware and we know you're working feverishly on your entries for that. Well, this month we have news of an even more exciting competition—one that MSDN Magazine is proudly co-sponsoring.

We hope you've turned to the Editor's Note first this month so we can tell you that the Connected Systems 2005 Developer Competition is currently accepting entries from the development community. If you're programming with Visual Studio 2005, SQL Server 2005, and/or BizTalk 2004/2006, and you've come up with something creative, this could be your big chance for fame and fortune. We know what you're saying: "But I write Microsoft Smartphone applications using Visual Studio 2005 and SQL Server Mobile Edition. Is this contest really for me?" As it happens, this is your lucky day.

Our competition is divided into 15 application categories—a measure of how far we've come in 20 years. Back in 1987, the categories would've been "best memory manager," "most annoying TSR," and "game that best uses all 16 possible screen colors." Now, look at your choices:

  1. Best extension of a SQL Server database using functionality provided by the CLR in SQL Server 2005
  2. Best integrated application using XML in SQL Server 2005 and Visual Studio 2005
  3. Best application using the Business Intelligence features of SQL Server 2005 (Reporting Services, Integration Services, Analysis Services)
  4. Best tool to provide additional functionality to SQL Server 2005 that is not already included in the product
  5. Best occasionally connected smart client application using SQL Server data
  6. Best integrated/SOA application utilizing SQL Server 2005, Visual Studio 2005, BizTalk 2004, or BizTalk 2006
  7. Best extension of a video gaming experience using Visual Studio 2005 and SQL Server 2005
  8. Best information worker solution using more than one Office system application and Visual Studio Tools for the Microsoft Office System 2005
  9. Best Microsoft Smartphone application using Visual Studio 2005 and SQL Server Mobile Edition
  10. Best use of Managed DirectX, Visual Studio 2005, and SQL Server Express
  11. Best Visual Studio 2005 PowerToy utilizing SQL Server Express
  12. Best extension of Visual Web Developer and the Personal Web Site Starter Kit; application must consume a publicly available Web service
  13. Best sample demonstrating key product functionality not already included in the BizTalk Server SDK
  14. Best adapter not already supplied by Microsoft or adapter vendors such as IWay
  15. Best implementation of a generic pattern (for example, request/response) on the BizTalk Server platform

There are lots of awards available to entrants, but the big pot is a cool $50,000*. (*Actual temperature of money may vary. Prize will most likely be awarded via an oversized novelty check.) The judges will choose one finalist from each of the 15 categories, and then select one overall winner. Novelty check, here we come.

Sound like it's up your alley? Well, what do you have to lose? Just remember, there is a time factor here: the final date for registration is August 30, 2005, and all entries must be submitted by September 15, 2005. There's a full list of rules and requirements on the official site, Connected Systems Developer Competition. Get coding!

One thing we've noticed in these rules is that, unfortunately, Microsoft employees are not eligible. We had developed the most amazing extension of a video gaming experience using Visual Studio 2005 and...okay, we admit it. We've got nothing. But if you have something, we hope to see your entry!

—J.T.

Thanks to the following Microsoft technical experts for their help with this issue: Kawarjit Bedi, Arjun Bijanki, Justin Brown, Christopher Brumme, Scott Densmore, Gert Drapers, Steve Lasker, Martyn Lovell, Andrew Pardoe, Yaniv Pessach, Mahesh Prakriya, Peter Provost, Elliot Rapp, Andy Rich, Daniel Roth, Jay Roxe, Vipul Shah, Joe Stegman, Michael Stuart, and Ravinder Vuppula.

ActiveX, DirectX, FrontPage, IntelliSense, JScript, Microsoft, MSDN, Visual Basic, Visual Studio, Windows, Win32, and Windows Server are either registered trademarks or trademarks of Microsoft Corporation. Other trademarks or tradenames mentioned herein are the property of their respective owners.

MSDN Magazine does not make any representation or warranty, express or implied with respect to any code or other information herein. MSDN Magazine disclaims any liability whatsoever for any use of such code or other information.


New Stuff: Resources for Your Developer Toolbox

New Stuff
Resources for Your Developer Toolbox
Marnie Hutcheson

Contents
Not Just Another Automated Web Test Tool
Quick Takes on Testing
Book Review

Not Just Another Automated Web Test Tool

Tired of spending oodles of time trying to reproduce bugs so you can then fix them? RadViewSoftware Inc. has a new addition to their Web application function and load testing tools that captures everything that goes on at the client and the server in synchronized logs. When something breaks or just slows down, you get all the information you need in order to see what caused the problem, without having to recreate the bug. This approach lets you find and remove the really subtle, irritating, hard-to-reproduce bugs that usually appear during production.

RadView offers a suite of four tools: WebFT, an automated capture replay tool for functional testing; TestView Manager, for managing tests through a central console; WebLOAD, for load, performance, and scalability testing; and the newest tool, WebLOAD Analyzer, which uses patented black-box technology to tell you exactly what happened, so you can solve the problem without having to recreate it.

RadView's tools all use JavaScript as their primary language, so tests that you create in WebFT can be reused in WebLOAD, the performance test tool. You can also customize your scripts using ActiveX® controls or by using WebFTs own functions. Or, you can keep it very simple and just let WebFT capture your tests and track them by using the default settings. You can always cut, copy, and paste elements from one script to another.

WebFT gives you a visual environment for capturing and creating test scripts (RadView calls them agendas) that has test recording, editing, debugging, verification, and reporting features. It records everything from the UI, including think time, plus all the object information, HTML, and so on.

There's a wizard to help you test Web services. You select a Web Services Description Language (WSDL) file and pick the methods and parameters you want to use. The tool creates the test agenda for you. There are several database test commands you can include as well.

During capture, everything is recorded at the object level so that scripts can work with dynamic Web sites. You can update the object properties manually, or you can choose to ignore any properties for replay. You can parameterize data in your captured agendas using the parameterization wizard. You can also assign output and input parameters to your test elements, and build or link to DataSets for your particular test agendas.

Verification is dependent on object type so, for example, a link in a table will have Text, Link, Table cell, and Table verifications. If it is an image, it could have image verification as well. When you define a verification, you set the severity level of the error, and define what to do if it fails. You can assign verification at the object level, the page level (assigns verifications to every object on the page), or at the global level (assigns the verification to all items on all pages). For example, it is possible to verify the title of a page against a value stored for that page.

TestView Manager lets you create and schedule test-tree plans that are comprised of your WebFT test agendas. It is a test management console that lets you create and run sophisticated test plans across multiple platforms and browser types, using any number of different tests.

TestView Manager has the ability to create summary reports of your test plan executions, giving you result reports at several levels of detail. You can start at the highest summary level and dig down to the individual test scripts and even down to the object level on individual pages. You can watch your test scripts execute through the test tree, viewing the execution log and the application screens as they are processed. When execution is complete TestView displays the test report. You also get a report of all pass/fail results for each test tree.

RadView's load and performance testing tool, WebLOAD, lets you take the tests you created in WebFT and use them to set up tests to emulate different types of browser settings and functional testing under load.

One of the most aggravating realities of software development is that few testers and fewer developers have the time or the tools to do enough testing to discover the error trends in their systems. Functional tests touch the major points, but they don't simulate what happens when real users start using your system. There are lots of tools available to find the "ultimate load," but I'm talking about simulating normal day-to-day business while you are still in the test phase.

You have to be able to run many tests repeatedly (just as users will) to determine the frequency or probability of occurrence of the "hard" bugs. And you need the absolute unbiased view of an automated replay report to tell you things like, "10 percent of all logons are failing." Otherwise, the human tester will just shrug it off and try their test again because they aren't testing logon specifically, they are testing something else, and even if they were inclined to report the failure, they can't because they can't reproduce it.

WebLOAD fills this requirement very well. It generates load for a single script or many different scripts according to the choices you make in the WebLOAD Wizard. You can run several different tests at the same time in various proportions. Throttle control lets you ramp the load up and down, so that you can simulate actual usage trends. You can have your users execute the next statement together or set your timers to let you know how long it takes to get from here to there. Your tests can use dynamic data and you can simulate connection speeds. You can save your load choices as a load session template so you can run this load series again.

WebLOAD collects and records detailed information on application infrastructure and correlates this information with user transaction activity and traffic bursts. You can create any number of reports while the test is running or after your tests have finished so you can get just the view or information you need. There are many different views available in the console and you can display several time-slice graphs in the same view as shown in the screen shot, or you can overlay multiple test sessions to compare their differences.

Clicking on any point in the graph lets you view the underlying activities. WebLOAD Analyzer gives you a synchronized view of the virtual user and system-level activity. For example, if you double-click on a spike for a Web page in the WebLOAD Console, you can then see in Analyzer all the operations that were executed for that particular Web page. Analyzer can also display and compare configuration settings, such as COM objects, registry settings, operating systems, and services running between two servers, side-by-side.

WebLOAD Analyzer collects and records detailed information on systems and application infrastructure and correlates this information with user transaction activity and traffic bursts. So, besides showing you the 50 worst performing operations in the run, it also lets you do things such as compare application and system settings to pinpoint configuration errors. If you have a problem server, you can compare its whole configuration to the configuration of your gold machine.

Test tools are a dime a dozen, so why make such a large investment in this software? These tools help you simply remove bugs rather than spend lots of time trying to find those bugs first. You save time and money, improve the quality of your code, and remove hard bugs before your system ever goes to production.

Price: TestView Manager (1 seat): $1,295; WebFT (1 seat): 1,295; WebLOAD (50 virtual users): $6,395; WebLOAD Analyzer (one console, two black boxes): $17,195.

www.radview.com

Quick Takes on Testing

Eventcorder Suite by CMS is a simple, inexpensive, basic capture replay toolset with automatic data input capability. It lets you automate routine tasks and perform simple or complex (scripted) automation, testing, and benchmarking.

Price: $49 per single-user license.

www.eventcorder.com

QA Wizard 3.1 by Seapine Software lets you perform automated functional testing of Windows® and Web-based applications. New features include support for the Microsoft® .NET Framework, script debugging, a Run Wizard, enhanced exception handling, and much more. QA Wizard uses a simple capture replay interface. It looks like this product is a good fit for not-so-technical function testing.

Price: $2,995 per single-user license.

www.seapine.com

ANTS Profiler 2.1 by Red Gate Software pinpoints slow code in .NET-based applications very quickly. It tracks memory managed by the common language runtime, breaking usage analysis into different objects and identifying each by name, size, and location. It generates snapshots of memory usage that show you the location of memory leaks.

Price: $295 per single-user license.

www.red-gate.com

Book Review

Buffer Overflow Attacks: Detect, Exploit, Prevent by James C. Foster, Vitaly Osipov and Nish Bhalla (Syngress, 2005) is an excellent book both for providing new information and for a handy desk reference. The authors give you line by line explanations of buffer overflow attacks, stack overflows, heap corruption, and format string bugs in Windows, Linux, and FreeBSD.

Don't think that just because your system is well secured that there isn't someone crafting a designer worm just for you. This book gives examples and case studies.

Price: $34.95.

All prices were confirmed at press time and are subject to change.

Send your New Stuff to newstuff@microsoft.com.

Marnie Hutcheson is president of Internet Development Associates (Ideva), a firm in Ocala, Florida, that specializes in Internet and intranet Web application design and development. She has published technical papers and books on various computing topics. You can reach her at marnie@ideva.com.


Extreme ASP.NET: Tools of the Trade: SQL Server Profiler and Query Analyzer

Extreme ASP.NET
Tools of the Trade: SQL Server Profiler and Query Analyzer
Rob Howard

Contents
Optimizing Data Access
SQL Server Profiler
Stored Procedures
Starting Profiler
SQL Server Query Analyzer
Analyzing a Query

In my last column, I discussed Microsoft® Application Center Test and how it could be used to measure the performance of your Web application (see Extreme ASP.NET: Tools of the Trade: Application Center Test). Application Center Test is one of those tools you don't know you need until someone shows you how useful it really is. I covered the value of Application Center Test for measuring performance, which is quite critical because you can use those measurements to make improvements, set goals, and most importantly plan capacity and scale.

In this column, I'm going to discuss two other tools of the trade: SQL Server™ Profiler and SQL Server Query Analyzer. While these SQL-oriented tools are not something most ASP.NET developers are familiar with, I would strongly encourage you to add these to your tool belt. Personally, if I could have worked on any other product team besides ASP.NET during my tenure at Microsoft, it would have been the SQL Server team. Internally, SQL Server is an amazingly complex piece of software, but programming and using it is quite easy. Understanding some of this complexity is important because, as you'll find, the database is often the bottleneck in your application and the more you know about the internals of SQL Server the more you can do to avoid those problems.

Optimizing Data Access

How much time do you spend analyzing how your application uses its database resources? For developers, the most common answer is not very much. Typically most performance problems are addressed in one of two ways: by adding new hardware or by tuning the code that runs the application. Most developers prefer to tune the code, but sometimes it actually can be more effective to just add new hardware. It should be noted, however, that performance problems can sometimes be worsened by adding new hardware before analyzing for bottlenecks. If a bottleneck is in the software, fast performing hardware can queue requests faster, making the bottleneck worse. The best approach, as always, is to analyze bottlenecks (hardware or software) before doing anything.

Being a developer, I prefer to sling some code first, before taking the hardware approach. Once that decision is made, it is usually a matter of doing some basic profiling of the application using Application Center Test and identifying where in the application performance is poor. Once the problem areas are identified, the next step should be to use SQL Server Profiler to inspect how the application uses database resources.

When it comes to writing high-performance Web applications, there is a performance truism: if your application performs any cross-process communication (database, Web services, remote object invocation) it's a better use of your time to either optimize those communications or to remove them entirely before attempting to optimize other code within your application. For database optimization, my general rule of thumb is if the application is going to the database more than once or twice on a given request (or making a single Web service call), there must be a very good reason for it because those roundtrips are most often the cause of poorly performing applications.

My team at Telligent has just spent the past three weeks tuning our Community Server for forums.asp.net. We made nearly 40 performance-related changes, of which 75 percent were in the database while the other 25 percent were directly related to code changes to either reduce or optimize the time spent in the database. The caching features of ASP.NET are one of the best tools for reducing database round-trips.

If you are using SQL Server you've already got a great set of tools, the Profiler and Query Analyzer, for understanding how your application is using the database. I won't cover all the internal workings of the Profiler or Query Analyzer in this column, but to learn more about how SQL Server works under the hood, I highly recommend the Microsoft SQL Server 2000 Performance Optimization and Tuning Handbook by Ken England (Digital Press, 2001). This book is one of my favorite references—my own copy is highlighted and coffee stained. Hopefully your copy will enjoy a similarly useful existence.

SQL Server Profiler

Reviewing logs or trace files generated by your application is invaluable for understanding how your app is being used. For example, for Web developers it is common to review IIS logs to understand app use. Within Community Server there is a built-in exception handler to trace all exceptions generated; later, we can review these logs and quickly get a good idea of the problem areas.

SQL Server Profiler can be used to view live sessions of activity on your running SQL Server database. You can glean important information such as how many queries your database is executing, how much time those queries are taking, which database is executing which query, and so on.

Figure 1 shows a profiler session with the database used on the asp.net cluster. You can see several captured SQL traces, which show stored procedures for weblogs.asp.net (items prefixed with blog_), beta.asp.net (items prefixed with aspnet_), Community Server forums (items prefixed with cs_), and download counters for www.asp.net downloads (IncrementDownloads). By selecting an item you can also view the full text, as has been done for the stored procedure cs_forums_Post. There are additional columns for CPU, Reads, Writes, and Duration.

Figure 1 SQL Server Profiler Session (Close Figure 1)

Stored Procedures

There is much passionate debate about using stored procedures. A good argument for using them can be found in "TheServerSide Debates: Stored Procedures v Parameterized Queries" by Peter DeBetta.

One of the benefits of stored procedures in enterprise applications is encapsulation and abstraction. If an application is using dynamic SQL (SQL text that is being generated on the fly), the application is typically more challenging to tune. Tuning dynamic SQL requires recompiling the application. Stored procedures, on the other hand, encapsulate their functionality and can be modified without affecting the public signature. Just as methods in your APIs are used for encapsulating functionality, stored procedures are in many ways the same capability, but in the database.

Of course there are also some good cases for using dynamic SQL. An example would be object/relational mappers that can dynamically create SQL based on the business objects requirements. If dynamic SQL was used in any of the asp.net clusters it would show up in SQL Profiler as standard SQL, like SELECT column1, column2 from TableA. However, there is none.

Starting Profiler

You can find Profiler with any standard installation of SQL Server in the SQL Server folder in the All Programs listing. I would also recommend installing SQL Server tools as part of any standard developer environment.

Once you open Profiler you will need to first connect to a database before you can begin a profiling session. To connect to the database, select File | New | Trace. This brings up the Connect to SQL Server dialog. Enter either your Windows or SQL Server credentials. Note that you will need to be a member of the SQL Server sysadmin group to run profiler.

Next, after the credentials have been verified you will need to set some properties for the trace in the Trace Properties dialog. By default, Profiler will capture events defined in the Standard template. This is an important subset of events rather than all of them. While capturing everything can be useful, most servers support more than one database and a trace that captures all database traffic isn't necessary or useful. By clicking on the Filters tab you can identify some filters to constrain what is captured. For example, you can constrain the traces to only show connections from a certain login, queries that have a specific duration, or queries that contain some known text (you can use wildcard symbols such as % to do SQL wildcard matching).

For example, let's say you wanted to run a trace of all database traffic for Community Server. Furthermore, you only wanted to look at communications that were taking over 100 ms to execute (usually items that take greater than 200 ms will be noticeable to the end user). In order to do this I set a filter with a specified login name, like csforums, and a duration greater than or equal to 100. Figure 2 shows the result of this trace.

Figure 2 Trace Results (Close Figure 2)

There are several stored procedures that are running more frequently, namely cs_forums_Search and cs_Sections_Get. Also of interest is cs_Threads_GetThreadSet which had a duration of 703! The cs_Sections_Get stored procedure also looks slightly abnormal with over 11,000 total reads along with a high duration value. These numbers aren't terrible, but they aren't perfect either.

Using the information that Profiler provides, you can at least begin to understand what your database is doing and where it is spending its time. In this trace session, I filtered for procedures that were taking a long time to run and focused on the forums database. There might also be cases in which there are too many queries running and slowing down the system, but in those cases it's best to alleviate some of the database load by adding an additional server or getting a local copy of the database for testing to remove the false positives.

On a heavily loaded system, rather than using the Profiler UI to collect your SQL traces, you should consider using T-SQL to send the results to a server-side file (you can still use the UI to create the trace definition, to script it, and to execute it). Sending results to server-side trace files ensures that no events are dropped, while sending trace events to a rowset (which is what SQL Profiler uses) does not guarantee this.

SQL Server Query Analyzer

Now that I've armed you with the basic ability to examine and profile your database you are probably wondering what happens next. Well, the next step is to take the queries and examine them in Query Analyzer, another wonderful tool that is useful for any developer working with SQL Server. As Profiler is useful for taking the 50,000 foot view of the system, Query Analyzer is the microscope used to analyze the details. Using Query Analyzer, you can execute queries and stored procedures and get a visual display of the SQL Server execution plan.

Similar to Profiler, Query Analyzer is found in the SQL Server folder in the All Program menu. It, too, requires that you complete the Connect to SQL Server dialog. Once connected, you can type the following to switch to your database:

use [databasename]
Next, click the play button on the menu bar (or highlight the text and press F5). Either option executes the highlighted SQL.

In Profiler I identified the stored procedure cs_Sections_Get as reading an unusually large amount of data—over 11,000 reads! The SQL trace from Profiler can be copied into Query Analyzer and you can then see exactly what that particular query is doing.

Analyzing a Query

To run the query in Query Analyzer, first paste the contents into Query Analyzer, then go to the toolbar and select Query | Show Execution Plan. Next, highlight the SQL to execute and press F5. Figure 3 shows my results.

Figure 3 Query Analyzer Results (Close Figure 3)

Immediately below the SQL query that was executed is the results grid, which shows what is returned by the query. In this case there are two resultsets returned. The first resultset is a list of Sections (forums) and the second one is a set of permissions. Community Server uses a role-based permissions system to control what users can or cannot do within the system. Whenever a list of Sections is retrieved, the system also refreshes its list of permissions for those sections.

At the bottom right of Query Analyzer you see the text: "5430 rows". This indicates the total number of rows returned. In this procedure, there was a bug in the permissions logic which would cause the system to return a list equivalent to total roles multiplied by the total sections. In the case of forums.asp.net, there are 29 unique roles and 181 sections; this totals 5,249 rows. Add the 181 rows returned by the first resultset and you get a grand total of 5,430 rows returned.

Obviously this is far too many rows. In fact, by using Profiler and Query Analyzer, this particular Query was optimized to return less than 250 total records—cutting the duration to less than 100 ms.

In addition to running queries, Query Analyzer allows you to analyze a query. Recall that you enabled the showing of the execution plan before executing the SQL. The plan provides a visual diagram of how SQL Server is executing your query, what indexes it uses, and other data that can help you optimize indexes or other data needed by SQL Server to enhance query performance.

Figure 4 shows each query executed, its execution plan, indexes used, joins, and the amount of data returned from each operation. Even the arrows between each item have significance: the thickness of the arrow is a visual indication of how much data is returned by that operation; that is, a poorly tuned query might have many thick arrows indicating operations which are likely returning too much data.

Figure 4 Execution Plans (Close Figure 4)

Application Center Test, SQL Server Profiler, and SQL Server Query Analyzer are some of the most powerful tools for tuning the performance of your ASP.NET or SQL Server application. Not only should they be part of a developer's tool belt, but these powerful tools should also be used routinely to inspect the application's overall performance. Too often developers will focus on optimizing routines within their code when eliminating or optimizing cross-process communications, such as database calls, would be a much better use of time.

Hopefully I've piqued your interest in learning more about the inner workings of SQL Server. It is a truly fascinating technology. If you are fortunate enough to have a DBA on your staff, ask them to show you how your application interacts with the database—you'll learn a lot!

Send your questions and comments for Rob to xtrmasp@microsoft.com.

Rob Howard is a founder of Telligent Systems, specializing in high-performance Web apps, knowledge management, and collaboration systems. Previously, Rob was employed by Microsoft where he helped design the infrastructure features of ASP.NET 1.0, 1.1, and 2.0. You can contact Rob at rhoward@telligentsystems.com.


Basic Instincts: Reflection in Visual Basic .NET

Basic Instincts
Reflection in Visual Basic .NET
Ted Pattison

Contents
Reflection Fundamentals
Reflecting Against an Assembly's Type Information
Conclusion

In the May 2005 Basic Instincts column I wrote about the Microsoft® .NET Framework support for custom attributes and attribute-based programming (see Basic Instincts: Designing With Custom Attributes). I discussed how the inclusion of attributes in the .NET Framework programming model gives Microsoft and third-party developers a powerful way to extend the metadata that is compiled into assemblies. This extensibility opens up opportunities for designs that leverage attribute-based programming.

The ability to programmatically inspect an assembly at run time is important for designing software based on custom attributes. It allows you to determine how your attributes have been applied and parameterized. This month I will focus on how you can use reflection to programmatically inspect an assembly at run time. I will also take some time to discuss other scenarios in which using reflection can be helpful.

Reflection Fundamentals
Figure 1 Metadata and Type Info
Figure 1 Metadata and Type Info

The .NET Framework introduced an important requirement to application development: every distributable unit of code (assembly) now requires high-fidelity metadata to describe itself and the types that are defined in it. Figure 1 depicts how that metadata and type information are contained within a compiled assembly.

Reflection is the act of programmatically inspecting an assembly, its metadata, and the type information that is contained within it. Getting started with reflection is quite simple. There's quite a bit you can do without writing very much code.

Reflection is made possible through a set of classes in the Framework Class Library as part of the System.Reflection namespace. Figure 2 shows an inheritance hierarchy of the classes that are commonly used when programming reflection. Note that the only two classes in this diagram that are not defined within the System.Reflection namespace are System.Object and System.Type.

Let's start by writing some code that shows you how to inspect the metadata associated with an assembly. For example, let's say you need to programmatically determine an assembly's four-part fully qualified name. You can get started by creating and initializing an object from the Assembly class.

Figure 2 Framework Class Library Reflection Classes
Figure 2 Framework Class Library Reflection Classes

The Assembly class provides a shared method named LoadFrom that makes it possible to initialize an Assembly object using the physical path to an assembly file. Once you have initialized the Assembly object, you can query for the complete name of the assembly using the FullName property.

You might have noticed that the class name Assembly appears inside of square brackets. These square brackets are required syntax in Visual Basic® .NET because Assembly is also a keyword. You need to use the square brackets to inform the Visual Basic .NET compiler that Assembly is being used as a class name rather than a keyword.

The example in Figure 3 initializes an Assembly object using a physical path and the LoadFrom method. Alternatively, you can use the Load method when you need to initialize an Assembly object from an assembly in the Global Assembly Cache (GAC) using an assembly's fully qualified name, as shown in the following code:

Dim asm As [Assembly]
Dim name As String = "AcmeCorp.BusinessLogic, " & _
                      "Version=1.0.24.0, " & _
                      "Culture=neutral, " & _
                      "PublicKeyToken=2bd1ac622c6e1de3"
asm = [Assembly].Load(name)

Figure 3 Retrieving an Assembly Name (Close Figure 3)
Imports System.Reflection

Class MyTestApp
  Shared Sub Main()
    '*** determine path to assembly DLL
    Dim AssemblyPath As String
    AssemblyPath = "C:\AcmeCorp\bin\AcmeCorp.BusinessLogic.dll"

    '*** create and initialize Assembly object, then query for name
    Dim asm As [Assembly] = [Assembly].LoadFrom(AssemblyPath)
    Console.WriteLine("Assembly Name: " & asm.FullName)
  End Sub
End Class

Note that the Assembly class provides three other shared methods for initializing Assembly objects in many other scenarios:

Dim asm1, asm2, asm3 As [Assembly]

'*** get the Assembly which contains this code
asm1 = [Assembly].GetExecutingAssembly()

'*** get the Assembly with the code that called this method
asm2 = [Assembly].GetCallingAssembly()

'*** get the assembly for the hosting application EXE
asm3 = [Assembly].GetEntryAssembly()

An Assembly object exposes the GetName method which returns an AssemblyName object. The AssemblyName object provides more granular information about the assembly name. For example, if you need to programmatically determine an assembly's simple name and version number, write the following code:

Sub GetAssemblyNameInfo(ByVal asm As [Assembly])

  '*** get AssemblyName object from Assembly object
  Dim asmName As AssemblyName = asm.GetName()

  '*** query AssemblyName object
  Dim SimpleName As String = asmName.Name
  Dim VersionNumber As String = asmName.Version.ToString()
  Dim BuildNumber As Integer = asmName.Version.Build

End Sub

You might ask why the information about an assembly's name has been factored out of the Assembly class and into the AssemblyName class. One reason has to do with the metadata contained within an assembly about its references to dependent assemblies. While the metadata for a referenced assembly does not contain enough information to initialize an Assembly object, it can provide what you need to initialize an AssemblyName object.

The metadata in every assembly contains a list of the other assemblies it references. When you reflect against an assembly, you can discover what other assemblies it references. You accomplish this by calling the GetReferencedAssemblies method of the Assembly class and enumerating through a collection of AssemblyName objects:

Shared Sub GetReferencedAssemblies(ByVal asm As [Assembly])
  Dim ReferencedAssembly As AssemblyName
  For Each ReferencedAssembly In asm.GetReferencedAssemblies
    Console.WriteLine(ReferencedAssembly.FullName)
  Next
End Sub

Reflecting Against an Assembly's Type Information

A common motivation for using reflection is to discover type information contained within an assembly at run time. This makes it possible to write utility applications that generate documentation about the public types and public methods contained within an assembly. It also makes it possible to determine how custom attributes have been applied to a class or method. Another common use is to dynamically instantiate classes and even to execute methods on those classes.

The System.Type class is the key for obtaining type information available through the Framework Class Library. When you want to programmatically discover type information, you need to acquire an instance of the System.Type class that has been initialized using a specific target type you want to inspect. An assembly object lets you enumerate through all its top-level types using a simple For Each loop to inspect System.Type objects one by one:

Shared Sub DisplayTypes(ByVal asm As [Assembly])
  For Each t As System.Type In asm.GetTypes
    Console.WriteLine(t.Name)
  Next
End Sub

You might wonder why the System.Type class is defined inside the System namespace while all the other reflection classes are defined within the System.Reflection namespace. One key factor that likely led to this design decision is the fact the System.Object class exposes a method named GetType which returns a System.Type instance. This means that every managed object and every managed value can provide you with an initialized System.Type instance that makes it easy to reflect against the type that was used to create it.

Let's look at a simple example. Imagine you have a reference that is holding some object and you want to determine the actual type of the object as well as the assembly in which it is contained. You can simply write a routine that looks like this:

Shared Sub DisplayTypeInfo(ByVal obj As Object)
 '*** get info about the object's declaring type
  Dim t As Type = obj.GetType()
  Console.WriteLine(t.FullName)

 '*** get info about assembly used to create object
  Dim asm As [Assembly] = t.Assembly
  Console.WriteLine(asm.FullName)
End Sub

As you can see, a System.Type object is what makes it possible to obtain type information. That's because each System.Type object acts as a gateway to the other reflection classes defined inside the System.Reflection namespace.

There are several other techniques for initializing a System.Type object using a specific managed type. For example you can use the Visual Basic .NET GetType statement. When you use the GetType statement, it requires that you provide a type name:

Dim t1 As Type = GetType(Integer)
Dim t2 As Type = GetType(AcmeCorp.BusinessLogic.Customer)

You can also use a shared method of the System.Type class named GetType. When calling this method you must pass a namespace-qualified type name as a string parameter (if you don't include assembly information as part of the type name, only types in the current assembly and in mscorlib.dll will be found):

Dim t As Type = System.Type.GetType("System.Int32")

One final technique for retrieving a System.Type object is to use the shared GetType method exposed by the Assembly class. This makes it possible to initialize a System.Type object from a type that you know is defined within a specific target assembly:

Dim AssemblyPath As String
AssemblyPath = "C:\AcmeCorp\bin\AcmeCorp.BusinessLogic.dll"
Dim asm As [Assembly] = [Assembly].LoadFrom(AssemblyPath)
Dim t As Type = asm.GetType("AcmeCorp.BusinessLogic.Customer")

When you call the GetType method in this fashion, you should remember that the type name must be passed as a namespace-qualified string parameter. Also keep in mind that if the type name you pass doesn't match the name of a type within the target assembly, the call to GetType does not throw an exception. Instead, it simply returns a null reference. It is key to check the return value of GetType and see if it equals Nothing in order to determine whether you have successfully retrieved the System.Type object you're after.

Once you have acquired a System.Type object, you can start reflecting against a specific type. The System.Type class exposes many useful public instance members that can provide information about the type itself as well as the type's members. Figure 4 is a simple example of querying for information about a type.

Figure 4 Get General Type Information (Close Figure 4)
Dim t As Type
t = GetType(AcmeCorp.BusinessLogic.Customer)

'*** display type name with namespace
Console.WriteLine(t.FullName)

'*** display type name without namespace
Console.WriteLine(t.Name)

'*** display namespace
Console.WriteLine(t.Namespace)

'*** determine when type is a class
Console.WriteLine(t.IsClass)

'*** determine when type is public
Console.WriteLine(t.IsPublic)

'*** display full name of containing assembly
Console.WriteLine(t.Assembly.FullName)

'*** display version number of containing assembly
Console.WriteLine(t.Assembly.GetName.Version.ToString)

Now let's say you want to find out about the fields, methods, properties, and events defined within a type. If you refer back to Figure 2, you will see that there is a class named MemberInfo which serves as a base class to more specific reflection classes such as MethodInfo, ConstructorInfo, FieldInfo, and PropertyInfo. These classes make it possible to get detailed information about the members defined within a class.

Let's look at a simple example of reflecting against the members of a type. Once you have initialized an instance of the System.Type class, you can simply call the GetMembers method and then enumerate through the returned collection of MemberInfo objects using a For Each loop:

'*** get System.Type object for Integer and discover
'*** the names of each public member
Dim t As Type = GetType(AcmeCorp.BusinessLogic.Customer)
For Each member As MemberInfo In t.GetMembers
  Console.WriteLine(member.Name)
Next
When calling the GetMembers method, you can also pass parameters to narrow the scope of the returned collection. For example, if you only want to retrieve the public instance members of a type, you can refine the call to GetMembers to look like this:
'*** get System.Type object for Integer
Dim t As Type = GetType(AcmeCorp.BusinessLogic.Customer)

'*** get an array of public instance members
Dim members As MemberInfo = _
    t.GetMembers(BindingFlags.Public Or BindingFlags.Instance)

'*** discover the name of each member
For Each member As MemberInfo In members
  Console.WriteLine(member.Name)
Next

If you want to retrieve a specific kind of member, you should note that the System.Type class provides several other instance methods such as GetConstructors, GetFields, GetMethods, GetProperties, GetEvents, and GetNestedTypes.

Now let's look at reflecting against a type to determine whether it has been defined with a specific attribute applied to it. In this example, I will use the sample custom attribute class named TestedAttribute that I presented in my previous column on authoring custom attributes. You can see the definition for the TestedAttribute type in the code listing in Figure 5.

Figure 5 Using the TestedAttribute Class (Close Figure 5)
Public Enum TestGrade
    Excellent
    Good
    Poor
    Unacceptable
End Enum

<AttributeUsage(AttributeTargets.All, Inherited:=False)> _
Public Class TestedAttribute : Inherits System.Attribute

    Public Tester As String
    Public Grade As TestGrade

    '*** parameterized constructor for passing parameters by position
    Public Sub New(ByVal Tester As String, ByVal Grade As TestGrade)
        Me.Tester = Tester
        Me.Grade = Grade
    End Sub

    '*** default constructor for passing parameters by name
    Public Sub New()
    End Sub

End Class

You should assume that the TestedAttribute has been applied to a custom class named Customer in the following manner:

<Tested("Bob", TestGrade.Excellent)> _
Class Customer
    '*** class definition omitted for brevity
End Class
Now imagine it's your job to write code that reflects against classes such as the Customer class to see whether they have been defined with the TestedAttribute. Furthermore, if you determine that a class such as the Customer class has been defined with the TestedAttribute, you would like to discover how this custom attribute was parameterized when it was applied.

You should begin by learning how to leverage an instance method exposed by the System.Type class named GetCustomAttributes. This method lets you determine if and how an attribute has been applied to a type. Figure 6 shows how to use the GetCustomAttributes method to discover whether TestedAttribute has been applied to the definition of the Customer class.

Figure 6 Using GetCustomAttributes (Close Figure 6)
'*** get type objects for type and custom attribute
Dim t As Type = GetType(Customer)
Dim attrType As Type = GetType(TestedAttribute)

'*** retrieve array of custom attributes
Dim results() As Object = t.GetCustomAttributes(attrType, False)

'*** determine whether attribute has been applied
If (results.Length >= 1) Then
  '*** get first element of array
  Dim attr As TestedAttribute = CType(results(0), TestedAttribute)

  '*** discover attribute parameter values
  Console.WriteLine("Tester: " & attr.Tester)
  Console.WriteLine("Grade: " & attr.Grade.ToString())
Else
  Console.WriteLine("TestedAttribute not present")
End If

Now that you have seen all the steps at once, I'm going to examine the preceding code passage in more detail. First, you should notice that the call to GetCustomAttributes involves two System.Type objects. You must initialize one System.Type object using the type you want to reflect against and a second System.Type object based on the type of the custom attribute you are looking for, as shown here:

Dim t As Type = GetType(Customer)
Dim attrType As Type = GetType(TestedAttribute)
Dim results() As Object = t.GetCustomAttributes(attrType, False)

Note that the second parameter passed to GetCustomAttributes is a Boolean value that indicates whether inheritance should affect the return value of GetCustomAttributes. In this case a value of False has been passed to indicate that I'm only interested in whether the attribute has been applied directly to the Customer class. In other words, it should not affect the outcome of the call to GetCustomAttributes if one of the base classes of the Customer class has been defined with the TestedAttribute class.

Next, consider the return value of the call to GetCustomAttributes, which returns an array of references to attribute objects. When the attribute you are looking for has not been applied to the class you are inspecting, a call to GetCustomAttributes returns an array with a length of 0. Your intuition as a programmer using Visual Basic might lead you to incorrectly anticipate a return value of Nothing. Therefore, you should always inspect the length of the array to see whether the attribute has been applied or not.

If the array returned by GetCustomAttributes has a length of 1 or greater, then you know that the attribute has been applied to the target class one or more times. However, many attributes including the TestedAttribute class do not allow themselves to be applied more than once to any one target. In such cases, you know the attribute has been applied exactly once. In cases where an attribute has been defined in such a fashion that it can be applied to a target class multiple times, you must look at the array length to determine how many times it has been applied to the target class.

In my scenario with TestedAttribute, a call to GetCustomAttributes will return an array with a length of 0 or a length of 1. If the length is 0, the attribute has not been applied. If the length is 1, the array returned by GetCustomAttributes holds a reference to a freshly created instance of the TestedAttribute class that has been initialized from metadata within the assembly that defined the Customer class. This makes it easy to retrieve the TestedAttribute object from the array and query for its parameterized values:

Dim attr As TestedAttribute = CType(results(0), TestedAttribute)
Console.WriteLine("Tester: " & attr.Tester)
Console.WriteLine("Grade: " & attr.Grade.ToString())

Conclusion

In this month's column, I introduced reflection and explained how you can use it to get information about an assembly and the types defined inside. The kinds of application and components that are possible with reflection are only constrained by your imagination. While my May 2005 column discussed how to author custom attributes and apply them, it did not tell a complete story. To unlock the power of custom attributes in software design, it's critical that you know how to use reflection. This makes it possible to determine how your custom attributes have been applied.

Send your questions and comments for Ted to instinct@microsoft.com.

Ted Pattison has been educating Windows-based developers as an author and trainer since 1990. Ted delivers hands-on training classes through PluralSight and provides consulting services through his company, Ted Pattison Group.


Data Points: The Enterprise Library Data Access Application Block, Part 2

Data Points
The Enterprise Library Data Access Application Block, Part 2
John Papa

Code download available at: DataPoints0508.exe (197KB)
Contents
Setting Up Your Project
Post-Build Events
Get Rows via a DataReader
Parameterized Queries and DataSets
Table Names and Exposure
ExecuteDataSet Overloads
Loading an Existing DataSet
Stored Procedures
Hit and Run
Another Hit and Run
Getting XML
Wrapping It Up

Last month I explored the foundation of the Enterprise Library Data Access Application Block (DAAB) including how it all fits into an architecture (see Data Points: The Enterprise Library Data Access Application Block, Part 1). I also discussed the Configuration Tool, how to use the cryptography and configuration features with the DAAB, and how the DAAB offers database abstraction through its database factory pattern and through reflection.

This month, I will continue the exploration by walking through several code examples that demonstrate the different ways data can be retrieved using the DAAB. I'll discuss how to get at features of ADO.NET through the DAAB and will show how to take advantage of the database-specific abstraction that the Enterprise Library DAAB offers through its database factory pattern.

I will start by setting up a project that uses the Enterprise Library DAAB and discussing how a project can benefit from post-build events. Then I will walk through examples that retrieve data using several different approaches which include returning the data in a DataReader, a DataSet, a string value, and XML.

I'll show you how to execute a parameterized query using inline SQL as well as stored procedures. The use of output parameters to retrieve data can be very efficient, as can the use of the ExecuteScalar method of ADO.NET (and of DAAB). I'll demonstrate how both of these can be accomplished, and explain methods to retrieve multiple rowsets at once. The DAAB is like any other tool in that to run efficiently it needs to be used properly. Therefore, along the way, I'll discuss how it can be tuned and how it operates under the covers. (All of the code samples can be downloaded from the MSDN®Magazine Web site.)

The Enterprise Library Application Blocks can be downloaded from the MSDN Web site at Enterprise Library. GotDotNet workspaces also is a good place to go to get the latest on Enterprise Library and any updates: Enterprise Library: Workspace Home.

Setting Up Your Project

For the following examples I will set up a Windows® Forms application that uses the Enterprise Library DAAB to retrieve and manipulate data against the SQL Server™ Northwind database. Figure 1 shows the finished project. (Note that the options to run each procedure that I will discuss here are shown on the left side of the dialog). First, I created a Windows Forms application using Visual Studio® .NET and I named it MSDN200508. I then added a reference to the DAAB and to the Configuration Application Block (since it is used by the DAAB). The Enterprise Library assemblies are located in the folder Program Files\Microsoft Enterprise Library\bin.

Figure 1 Testing DAAB Using a Windows Form (Close Figure 1)

The Enterprise Library uses the Web.config file to point to its configuration files for ASP.NET applications. In a Windows Forms application, the Enterprise Library uses the app.config file to point to its configuration files. After I created my Windows Forms application, I added an app.config file to the project. Next, I opened the Enterprise Library configuration tool, selected File | Open Application from the menu and chose the app.config file of the MSDN200508 Windows Forms project.

From there, I set up my DAAB config file to point to the Northwind database on my local SQL Server instance, and because I am security conscious I also encrypted the dataConfiguration.config file. (For details on how to use the Configuration Tool to set up a DAAB config file to point to the Northwind database and how to encrypt it with cryptography, see my Data Points column in the July 2005 issue.) Once I have my configuration files established, I added the dataConfiguration.config file to my Windows Forms project. Then, just to test things out, I added a button and a DataGrid to a form, added the following code to the button1_Click event, and built and ran it:

private void button1_Click(object sender, System.EventArgs e)
{
    string sSql =
        "SELECT CustomerID, CompanyName, City, Region FROM Customers";
    Database dbNorthwind = DatabaseFactory.CreateDatabase();
    DBCommandWrapper cmdCust =
        dbNorthwind.GetSqlStringCommandWrapper(sSql);
    DataSet dsCust = dbNorthwind.ExecuteDataSet(cmdCust);
    dataGrid1.DataSource = dsCust.Tables[0].DefaultView;
}

This code is supposed to get the list of customers from the database and load the DataGrid with them. However, instead I get an exception that tells me it cannot find the dataConfiguration.config file. The reason this occurs is that when you build the Windows Forms application and run it in debug mode, the assembly's executable file (MSDN200508.exe) and its config file (MSDN200508.exe.config) are created in the bin\debug folder of the project. The MSDN200508.exe.config file tells the application to look for the dataConfiguration.config file in the same folder where the app resides. By default, the configuration tool does not include any path information to the dataConfiguration.config file (nor any other of the Enterprise Library config files). Since I did not specifically add the path, when the application was run in debug mode it created the exe and the exe.config files in the bin\debug folder. Then it tried to locate the dataConfiguration.config file in the bin\debug folder and threw an exception when it was not found there.

Note that this is not an issue with ASP.NET Web applications. When ASP.NET applications are built, the Web.config file remains in the root folder of the Web application.

Post-Build Events

The solution I chose is to use a post-build event to make sure that all of my Enterprise Library configuration files are copied to the target folder when the project is built. Visual Studio .NET exposes pre-and post-build events for C# projects but not for Visual Basic® .NET projects. When I develop Visual Basic .NET projects I create a .bat file that copies the configuration files to the target directory. Then I manually run the .bat file after each build. (Yes, this is annoying but the good news is that build events appear in Visual Studio 2005. Moreover, in Visual Studio 2005 you can automatically copy files to the output directory by configuring the "Copy to Output Directory" property for the appropriate files.) For Visual Basic .NET projects, I included a sample .bat file that you can download along with the code from this column. Since the project I created is in C#, I added a post-build event (see Figure 2) which you can also perform by following these four quick steps:

  1. In Solution Explorer, right-click the project and select Properties
  2. Select Common Properties | Build Events
  3. Left-click in post-build event command line
  4. Enter the following command: copy "$(ProjectDir)*.config" "$(TargetDir)", and then click OK

Figure 2 Creating a Post-Build Event (Close Figure 2)

When the project is built, it will copy all files in the project directory with an extension of .config to the target directory. If I make a debug build, the config files are copied to the bin\debug folder. If I make a release build, the config files are copied to the bin\release folder. If the build succeeds, the last message in the output window will be a success message from the post-build event.

As an alternative for Visual Basic .NET projects, pre-and post-build events can be created using an add-in for Visual Studio .NET. It can be downloaded from this Web address: Visual Studio .NET 2003 Automation Samples.

Get Rows via a DataReader

To get started with the Enterprise Library DAAB, I first need to set up the means to connect to a database. The DatabaseFactory class exposes a CreateDatabase method which creates an instance of a Database object (Microsoft.Practices.EnterpriseLibrary.Data.Database, to be exact). CreateDatabase has two signatures. If it is passed an instance name, it uses the matching instance name found in the dataConfiguration.config file. If no instance name is passed (as shown in the first lines of code in Figure 3), it uses the default instance defined in the dataConfiguration.config file.

Figure 3 Calling the DAAB ExecuteReader (Close Figure 3)
public string GetCustomersViaExecuteReader()
{
    // Create the Database object using the default database as
    // indicated in dataConfiguration.config.
    Database db = DatabaseFactory.CreateDatabase();

    // Set the SQL statement
    string sSql = "SELECT CustomerID, CompanyName, "
        + " City, Country  FROM Customers";

    // Create the DBCommandWrapper and execute the SQL
    DBCommandWrapper cmd = db.GetSqlStringCommandWrapper(sSql);

    // Using "using" will cause both the DataReader and connection to be
    // closed. (ExecuteReader will close the connection when the
    // DataReader is closed.)
    StringBuilder sbData = new StringBuilder();
    using (IDataReader dataReader = db.ExecuteReader(cmd))
    {
        while (dataReader.Read())
        {
            sbData.Append(dataReader["CompanyName"]);
            sbData.Append(Environment.NewLine);
        }
    }
    return sbData.ToString();
}

The instance of the Database class that is created can be used to access a variety of data providers including SQL Server and Oracle. The type of provider defined in the dataConfiguration.config file determines the type of database that the instance of the Database class will reference. By using this factory pattern, you can better abstract the data provider than if you use the specific namespaces, such as System.Data.SqlClient.

After I create an instance of the Database class, I use its GetSqlStringCommandWrapper method to create an instance of a DBCommandWrapper. The GetSqlStringCommandWrapper method accepts command text such as a SQL statement and creates an instance of a DBCommandWrapper. In this project, the DBCommandWrapper wraps the functionality of the ADO.NET SqlCommand object (or whatever provider-specific Command object is relevant to the data provider). Now I pass the DBCommandWrapper to the Database.ExecuteReader method to return a DataReader. I then loop through the rows using the DataReader and build a string containing the list of customers using a StringBuilder. (Since string variables are immutable, using StringBuilder is more efficient than using a string variable when strings are continually concatenated together.)

The connection is opened when the ExecuteReader method is invoked. If I used ADO.NET code directly, I would first have to open the connection and then I could issue the ExecuteReader method of the Command object to get a DataReader. With the DAAB, the ExecuteReader method opens the connection for me and then gets the DataReader. Whether I use ADO.NET code directly or the DAAB, the connection is left open after the ExecuteReader method is invoked. It is very important to make sure that the connection is closed to conserve resources.

In ADO.NET, I can either explicitly close the connection when I'm finished using the DataReader or I can tell the DataReader to close the connection when the DataReader closes. The code to do this in ADO.NET is:

SqlDataReader rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection);

As a nice cleanup feature, under the covers the DAAB Database.ExecuteReader method invokes the ADO.NET Command object's ExecuteReader method with the CommandBehavior.CloseConnection enumerator value. In the code in Figure 3, instead of closing the DataReader explicitly, I used the using statement to close the DataReader. When the using statement's block completes, the DataReader is closed, closing the connection with it. Figure 1 was the result of running the code in Figure 3.

Parameterized Queries and DataSets

When using a DataReader, the DAAB opens the connection for me, then leaves it open until the DataReader is closed. However, when invoking the ExecuteDataSet or LoadDataSet methods to fill a DataSet, the DAAB opens and closes connections for me. The Database.ExecuteDataSet method creates a new DataSet and fills it with one or more DataTable objects from a database. The following code sample creates a DBCommandWrapper object that represents a parameterized SELECT statement:

public DataSet GetCustomersViaExecuteDataSet(string sCountry)
{
    Database db = DatabaseFactory.CreateDatabase();
    string sSql = "SELECT CustomerID, CompanyName, City, Country" +
        " FROM Customers WHERE Country = @sCountry";
    DBCommandWrapper cmd = db.GetSqlStringCommandWrapper(sSql);
    cmd.AddInParameter("@sCountry", DbType.String, sCountry);
    return db.ExecuteDataSet(cmd);
}

The DBCommandWrapper exposes a few methods to add parameters: AddParameter, which adds a parameter (no direction specified); AddInParameter, which adds an input parameter; and AddOutParameter, which adds an output parameter.

The code uses the AddInParameter method to add the @sCountry parameter and its value to the DBCommandWrapper. (I could have used the AddParameter method and then separately set the direction to be an input parameter, as well.) Then I call the ExecuteDataSet method of the Database object, which executes the DBCommandWrapper's command, retrieves the results, creates a new DataSet, and throws the results into the new DataSet. The DataSet will contain a single DataTable containing the rowset of customers. The DBCommandWrapper code can be run from the sample project by choosing the listbox item #2.

Table Names and Exposure

One caveat with the ExecuteDataSet is that it names the first DataTable "Table". If the DBCommandWrapper's command returned two rowsets, the second rowset would have been put inside of a second DataTable named "Table1" (the third DataTable will be named "Table2", and so on).

One ingredient that I demand of any data layer or data access component is the ability to communicate with the underlying data access technology (ADO.NET, in this case). While DAAB makes it easier to create a command and add parameters to it, if I need to make an adjustment that the DAAB interface does not expose, I can always go right to the ADO.NET objects. For example, if I want to set a property of the ADO.NET SqlParameter object I can get to it by going through the following line of code:

SqlParameter param = (SqlParameter)cmd.Command.Parameters["@sCountry"];

Concatenating user-supplied parameters into SQL strings and then executing the SQL makes an application vulnerable to SQL injection attacks. It is a good practice to use parameterized queries or stored procedures with parameters whether you use inline SQL or stored procedures. One nice feature of the Enterprise Library DAAB is that if it detects that the parameter token (the @ character for SQL Server or the : character for Oracle) is not the first character of the parameter name, it will append the appropriate data provider parameter token automatically.

ExecuteDataSet Overloads

The ExecuteDataSet also has other overloaded methods that allow for a variety of behaviors. For example, some of the overloaded versions of ExecuteDataSet allow you to pass in an IDBTransaction object that the DBCommandWrapper should enlist in. Others allow command text and the type of command to be passed in instead of an explicit DBCommandWrapper object. In this case the ExecuteDataSet method will generate the DBCommandWrapper object under the covers from the Database object using the command text and command type parameters. Another set of overloads for ExecuteDataSet allows for the name of a stored procedure to be passed in as well as a parameter array for the values of the parameters for the stored procedure.

Loading an Existing DataSet

While the Database.ExecuteDataSet method fills a new DataSet with data, the Database.LoadDataSet method fills an existing DataSet. As shown in the following code, the LoadDataSet method accepts as parameters the DBCommandWrapper to execute, an existing DataSet, and the name to use for the DataTable that it will create and fill with the results from the DBCommandWrapper:

string sSql = "SELECT CustomerID, CompanyName, City, Country" +
    " FROM Customers WHERE Country = @sCountry";
Database db = DatabaseFactory.CreateDatabase();
DBCommandWrapper cmd = db.GetSqlStringCommandWrapper(sSql);
cmd.AddInParameter("@sCountry", DbType.String, sCountry);
DataSet ds = new DataSet();
db.LoadDataSet(cmd, ds, "Customers");

In this example, the DataSet called ds is created prior to the invocation of the Database.LoadDataSet method. This comes in handy when I want to fill an existing DataSet that already contains one or more DataTable objects or when I want to use a typed DataSet. The code in Figure 4 can be run from the sample project by choosing the listbox item #3.

Figure 4 Loading a DataSet with Multiple DataTables (Close Figure 4)
public DataSet GetCustomersViaLoadDataSetMultipleTables(string sCountry)
{
    Database db = DatabaseFactory.CreateDatabase();
    string sSql = "SELECT CustomerID, CompanyName, City, Country" +
        " FROM Customers WHERE Country = @sCountry;" +
        "SELECT OrderID, CustomerID, OrderDate FROM Orders" +
        " WHERE CustomerID IN (SELECT CustomerID" +
        " FROM Customers WHERE Country = @sCountry)");
    DBCommandWrapper cmd = db.GetSqlStringCommandWrapper(sSql);
    cmd.AddInParameter("@sCountry", DbType.String, sCountry);
    DataSet ds = new DataSet();
    db.LoadDataSet(cmd, ds, new string[] {"Customers", "Orders"});
    ds.Relations.Add("Customers2Orders",
        ds.Tables["Customers"].Columns["CustomerID"],
        ds.Tables["Orders"].Columns["CustomerID"]);
    return ds;
}

If I want to execute a command that returns multiple rowsets, I would use the overloaded signature for LoadDataSet that accepts the DBCommandWrapper, the existing DataSet, and an array of the table names (one for each expected resultset). The code in Figure 4 shows how to do this by first creating a string containing two SELECT statements concatenated with a semicolon. Then it creates a DBCommandWrapper out of the SQL string and passes in a value for the @sCountry parameter. Finally, the code invokes the LoadDataSet method, passing to it the DBCommandWrapper, the DataSet, and the string array containing the values to use as the names of the two DataTables to be created within the DataSet. The code sample in Figure 4 can be run from the sample project by choosing the listbox item #4.

One difference between ExecuteDataSet and LoadDataSet is that LoadDataSet has a parameter for the name(s) of the DataTable(s) it will create. As I mentioned earlier, the ExecuteDataSet method names each DataTable starting with Table, Table1, Table2, and so on. Another difference between the two methods is that LoadDataSet expects to load an existing DataSet while ExecuteDataSet expects to generate a new one.

Stored Procedures

The code samples shown so far can all be modified to execute stored procedures instead of inline SQL code. In the following code snippet, which can be run by executing listbox item #5 from the sample project, the DBCommandWrapper's GetStoredProcCommandWrapper method is passed the name of the stored procedure to execute:

DataSet ds = new DataSet();
string sProc = "prGetCustomersAndOrders";
Database db = DatabaseFactory.CreateDatabase();
DBCommandWrapper cmd = db.GetStoredProcCommandWrapper(sProc);
cmd.AddInParameter("@sCountry", DbType.String, sCountry);
db.LoadDataSet(cmd, ds, new string[] {"Customers", "Orders"});
ds.Relations.Add("Customers2Orders",
    ds.Tables["Customers"].Columns["CustomerID"],
    ds.Tables["Orders"].Columns["CustomerID"]);

Under the covers this method creates a command, sets its command type to be a stored procedure and assigns the name of the stored procedure to the command. This example produces the same results as the previous example that used inline SQL. The two SELECT statements that are in the inline SQL in the previous example are now contained within the stored procedure in this example. Figure 5 shows the results of running listbox item #5.

Figure 5 Loading Two Data Tables into a DataSet (Close Figure 5)

Hit and Run

When I want to retrieve a handful of values from a single row, returning a rowset and creating a DataSet from it can be overkill. If I do not need a DataSet, but just the values from a few columns, calling a stored procedure and having it return the values as output parameters is a good option. I call this the hit-and-run approach to getting a row's data since it hits the database, gets the values it needs, and returns without creating a data store such as a DataSet. For the next example I will use the stored procedure prGetCustomerViaOutputParameters, as shown in Figure 6.

Figure 6 Stored Procedure Returning Output Parameters (Close Figure 6)
ALTER PROCEDURE dbo.prGetCustomerViaOutputParameters
    (
        @sCustomerID NCHAR(5),
        @sCompanyName NVARCHAR(40) OUTPUT,
        @sCity NVARCHAR(15) OUTPUT,
        @sCountry NVARCHAR(15) OUTPUT
    )
AS
    SELECT  @sCompanyName = CompanyName,
            @sCity = City,
            @sCountry = Country
    FROM    Customers
    WHERE   CustomerID = @sCustomerID
    RETURN

The Database.ExecuteNonQuery method will execute a command (such as a stored procedure or an inline SQL statement) and not return a DataSet or a DataReader. ExecuteNonQuery is great for executing action queries when there is no rowset to return or when only the output parameters of a stored procedure need to be accessed. The code in Figure 7 sets up a DBCommandWrapper to represent the stored procedure shown in Figure 6 and assigns it its input parameter. It then assigns three output parameters (using the AddOutParameter method) to the DBCommandWrapper, one for each of the output parameter values that the stored procedure will return. When the Database.ExecuteNonQuery method is invoked, the stored procedure is executed and the output parameters are filled with values. At this point I can retrieve the values from the output parameters and return them to the Windows Form. (The Figure 7 code sample can be run from the sample project by running listbox item 6.)

Figure 7 Retrieving Output Parameters (Close Figure 7)
public string GetCustomerViaOutputParameters()
{
    Database db = DatabaseFactory.CreateDatabase();
    string sProc = "prGetCustomerViaOutputParameters";
    DBCommandWrapper cmd = db.GetStoredProcCommandWrapper(sProc);
    cmd.AddInParameter("@sCustomerID", DbType.String, "ALFKI");
    cmd.AddOutParameter("@sCompanyName", DbType.String, 40);
    cmd.AddOutParameter("@sCity", DbType.String, 15);
    cmd.AddOutParameter("@sCountry", DbType.String, 15);
    db.ExecuteNonQuery(cmd);

    StringBuilder sbData = new StringBuilder();
    sbData.Append(cmd.GetParameterValue("@sCompanyName").ToString());
    sbData.Append(Environment.NewLine);
    sbData.Append(cmd.GetParameterValue("@sCity").ToString());
    sbData.Append(Environment.NewLine);
    sbData.Append(cmd.GetParameterValue("@sCountry").ToString());
    sbData.Append(Environment.NewLine);
    return sbData.ToString();
}

Another Hit and Run

Sometimes all that is needed from a stored procedure or an inline SQL statement is a single value. In this case using the Database.ExecuteScalar method is very efficient. ExecuteScalar can be performed on a DBCommandWrapper's command whether it is a SQL statement or a stored procedure. Ideally, the command would return a single row and column from which ExecuteScalar would extract the single value. This code sample can be run from the sample project by running listbox item 6:

Database db = DatabaseFactory.CreateDatabase();
string sProc = "prGetCustomerName";
DBCommandWrapper cmd = db.GetStoredProcCommandWrapper(sProc);
cmd.AddInParameter("@sCustomerID", DbType.String, "ALFKI");
object test = db.ExecuteScalar(cmd);
return test == null ? String.Empty : test.ToString();

The ExecuteScalar method returns an object; therefore it must be converted to the datatype that you expect it to return. In this particular case, I knew it was going to return a string. However I could have used the System.Convert method in order to convert the return value to Int32, for example, if I expected to get back a quantity or a row count.

Getting XML

The DAAB can also be used to retrieve XML via an XmlReader object. Figure 8 shows how the SqlDatabase.ExecuteXmlReader method can be used to execute a SQL Server stored procedure that returns XML. The stored procedure uses the FOR XML AUTO clause to return the data in XML format as opposed to the standard set-based rowset. (See the Data Points column in the June issue of MSDN Magazine for more information on SQL Server FOR XML clause.)

Figure 8 Getting XML from a Stored Procedure (Close Figure 8)
public string GetCustomersViaExecuteXmlReader()
{
    SqlDatabase db = (SqlDatabase)DatabaseFactory.CreateDatabase();
    string sProc = "prGetCustomersXml";
    SqlCommandWrapper cmd = (SqlCommandWrapper)
        db.GetStoredProcCommandWrapper(sProc);
    cmd.AddInParameter("@sCountry", DbType.String, "USA");
    XmlReader xmlRdr = null;
    StringBuilder sbData = new StringBuilder();
    try
    {
        xmlRdr = db.ExecuteXmlReader(cmd);
        while (!xmlRdr.EOF)
        {
            if (xmlRdr.IsStartElement())
            {
                sbData.Append(xmlRdr.ReadOuterXml());
                sbData.Append(Environment.NewLine);
            }
        }
    }
    finally
    {
        if (xmlRdr != null)
        {
            xmlRdr.Close();
        }

        // Explicitly close the connection. The connection is not closed
        // when the XmlReader is closed.
        if (cmd.Command.Connection != null)
        {
            cmd.Command.Connection.Close();
        }
    }
    return sbData.ToString();
}

It is important to note that the code in Figure 8 uses SQL Server provider-specific classes. Since the FOR XML clause is not available in all data providers (or an equivalent means to retrieve XML directly), I used the SqlDatabase and SqlCommandWrapper objects. (This code sample can be run from the sample project by executing listbox item #8.)

Notice that I explicitly close the XmlReader and the Connection when I am finished with the XmlReader. This implementation differs from how I used the DataReader in Figure 3. The DataReader implements the IDispose interface, so I can use it in a using block; when the using block ends, the DataReader is automatically closed. The DAAB closes the Connection when the DataReader is closed, so the code in Figure 3 tidies itself up nicely. The XmlReader does not implement the IDispose interface so I cannot use it in a using block and must close the XmlReader explicitly. Further, the ExecuteXmlReader method will not close the Connection when the XmlReader is closed, so I have to close the Connection explicitly.

Wrapping It Up

The Enterprise Library DAAB offer a variety of ways to retrieve data from a data provider using ADO.NET. It also exposes several different overloaded methods for each of the four main data retrieval methods on the Database object. Whether using the DAAB, a custom data access layer, or straight ADO.NET, it is important to make sure to close all connections. The Enterprise Library DAAB helps open and close connections in many cases but when using the XmlReader and DataReader objects it pays to check that connections are closed.

In the first part of this series in the July issue, I discussed how the Enterprise Library DAAB works, how it can be configured, and how it uses cryptography. In this second part I reviewed several ways in which it can be used to retrieve data. In the final installment of this series I will demonstrate several ways the Enterprise Library DAAB can modify data, how to implement transactions with it, how it can use parameter caching, and how to set up NUnit to test the data access code.

Send your questions and comments for John to mmdata@microsoft.com.

John Papa is a Senior .NET Consultant with ASPSOFT and a baseball fanatic who spends most of his summer nights rooting for the Yankees with his family and his faithful dog, Kadi. He has authored several books on ADO, XML, and SQL Server and can often be found speaking at industry conferences such as VSLive or blogging at codebetter.com/blogs/john.papa.


Cutting Edge: DataSets vs. Collections

Cutting Edge
DataSets vs. Collections
Dino Esposito

Contents
Where's the Problem?
DataSet—The Good Side
DataSet—The Dark Side
Typed DataSet—Good and Bad
Custom Entities and Collections
Make Your Choice
When to Use Which
Wrap Up

In software, five years is like a geological era. Five years ago, the Microsoft® .NET Framework had just been announced. Since then, the DataSet has emerged as the key object for performing a variety of data-related tasks in .NET-based applications. Five years ago, the DataSet was hailed as a greatly enhanced version of the ADO Recordset. How would you have designed a data access layer (DAL) before the advent of the .NET Framework? I'm sure you would have built it around ADO and its almighty Recordset object, which was disconnected and XML-serializable.

Few developers were employing custom collections, implementing a true object-oriented programming model, or pursuing strongly typed data access at a time when object-oriented programming was either a feature for a few C++ programmers or an advanced trick for the brave developer using Visual Basic®.

Fortunately there are now better options for building an enterprise system back-end, but you still have to choose the right approach for the job. Let's explore how you can best make the decision.

Where's the Problem?

Imagine you need to architect a multitier, distributed .NET-based app with three logical layers—presentation and interface services, business logic with core functionalities, and data access, where all database stuff and messaging happens. For this type of app, layering is the key. If the lowest layer could be completely ignorant of the upper layers, the system would be nearly perfect.

When designing a layered system you must consider factors such as the ability to cascade changes (such as new data schemas) through layers and the amount of overhead involved when data is moved from one layer to the next. Plus you need a business tier to execute your business logic, and you need a DAL to provide Create/Read/Update/Delete (CRUD) functions to the rest of the system. There are two primary ways to provide this functionality—by using a commercial Object/Relational (O/R) mapping tool or by rolling your own DAL.

An O/R mapper works by mapping custom objects to entities into a relational database. Through an XML file or any other form of a settings container, the tool shields you from relying on any knowledge of the underlying database. You work with domain-specific objects provided to you by the mapper. On the opposite end of the spectrum is the other more common approach, building your own DAL. Here your primary problem is making a decision about the mechanism used to pass data around.

All things considered, I'd say that four options are worth spending some time on: DataSets, typed DataSets, custom collections, and plain XML. Using plain XML text is the least attractive option even though it provides full integration with any other platform. It is weakly typed, hard to maintain, performs poorly, and is not a powerful programming interface in the context of an enterprise app. Now let's review the pros and cons of the other three options.

DataSet—The Good Side

Frankly, writing data access code is rarely fun. In fact, it's frequently boring and tedious, and it requires you to think relationally. So when Microsoft introduced the DataSet with the first version of the .NET Framework everybody looked at the new API with great interest because the DataSet and its companion classes were more intuitive. In addition, there are lots of wizards and designers in and around Visual Studio® .NET to generate code and inject it directly into source files.

The DataSet is designed to be a general-purpose container of any information that can be expressed in a tabular format—a container of relational information like that resulting from a database query. The DataSet looks and behaves like an in-memory database. However, it has no notion of connection strings, commands, stored procedures, hosts, and logins. It is purely a container class that stores tables of data and allows relations to be defined between pairs of contained tables, which in turn can include constraints, build indexes, and perform data retrieval and filter operations. It also supports the concept of computed expression columns in tables.

The DataSet was not specifically designed to work with databases, but it does fit well into a pure database scenario. The DataSet was designed to be a datacentric container which you can populate with tabular data from virtually any source—the file system, memory, real-time devices and, of course, database queries.

ADO.NET provides a family of objects that bridge DataSets to databases in an intelligent way. These objects are known as data adapters. By calling methods on a data adapter, you can execute database commands using the contents of the DataSet as the input (batch update) or as the output stream. However, regardless of the .NET Framework facilities for connecting DataSets and databases, the DataSet class remains a datacentric object with a database-oriented programming model.

With a DataSet object you can easily pack and transmit any sort of data and combine interrelated data from different sources and tables. In addition, the DataSet is serializable, has integrated XML capabilities, has built-in support for optimistic concurrency, and the ability to define and handle complex relationships between contained tables. By using the DataSet to represent data, you don't need to change anything in your DAL API should the database schema change. Using a DataSet also allows you to take advantage of data binding in both Web Forms and Windows® Forms.

If you think of your application design with a SQL Server™ or ADO.NET mindset, opting for the DataSet is a natural choice, and probably the only one you would ever think of.

DataSet—The Dark Side

In light of the favorable characteristics of the DataSet, using it in enterprise applications is definitely a reasonable choice, but consider the drawbacks before you make a final decision. Sometimes strengths can also be weaknesses. Take serialization and deserialization, for example.

The DataSet implements a serialization algorithm that performs less than optimally as the DataSet grows to thousands of rows. In the .NET Framework 1.x, the DataSet serializes itself to XML, resulting in a verbose data stream padded with schema information. The serialization algorithm has been radically improved in the .NET Framework 2.0, as I discussed in the October 2004 installment of Cutting Edge (Cutting Edge: Binary Serialization of DataSets). In this new version, the DataSet serialization can take place through a full binary stream, thus saving serious bandwidth. Additionally, typed DataSet serialization can be further optimized by setting the type DataSet.SchemaSerializationMode property to SchemaSerializationMode.ExcludeSchema. When this option is set, the serialized payload does not contain schema information, resulting in a smaller payload.

Figure 1 compares times for the classic XML-driven approach with a true binary serialization approach. As you can see, as long as the number of rows remains in the hundreds, the two approaches don't differ significantly in speed. When the row count enters the thousands, however, the gap grows significantly.

Figure 1 Remoting End-to-End Time
Figure 1 Remoting End-to-End Time

Aside from serialization, the most significant problem with the DataSet is that it is too generic and too polymorphic a container, and it transfers this trait to the system architecture you're planning. DataSets don't represent data; they just contain it.

Here's the problem. Representing data means that the object exposes a programming interface to describe a given entity. The interface models the characteristics and attributes of the entity and provides methods to implement the expected behavior. Containing data means that the object incorporates all the attributes of a given entity, but can't provide any specific additional behavior. In complex scenarios, where you need to employ smarter objects with their own behavior that are fully representative of business entities, the DataSet is simply the wrong tool. It's the proverbial hammer that makes everything look like a nail.

As a general-purpose, serializable, disconnected data container, the DataSet is at home in the .NET Framework where it can serve in a variety of realistic scenarios. Having a DataSet class in the .NET Framework makes a lot of sense; having DataSets at work in a complex enterprise application with articulated business logic and interoperating entities may not.

Does this mean that the DataSet doesn't belong in any multitier system? No. Functionally speaking, using the DataSet doesn't limit your programming power. However, if your system is rich in interoperating business entities, there might be more effective tools that you can use for the job.

Finally, because DataSets contain data but don't represent business entities, you need additional components to manipulate DataSets and you are forced to embrace a database-oriented programming model. This is not necessarily bad as long as it's your decision and you're not forced into it unwittingly by wizards and IDEs.

Other considerations apply if the architecture includes Web services. DataSets should not be used with Web services or, at least, they should not be the sole possible return value of a Web method. You should use Web method overloading (each overload of the same functionality with its own exposed name) and return the same data in other simpler formats. The polymorphic nature (and inherent complexity) of DataSets often confuses any client that is not part of .NET and also violates at least one of the four service-oriented architecture (SOA) tenets—autonomy of the (Web) services. For additional guidance on Web service interoperability, see Web Services Interoperability.

Typed DataSet—Good and Bad

Aware of the logical limitations of the DataSet object, Microsoft also introduced the typed DataSet—a class that derives from DataSet and inherits all the members of a DataSet. In addition, a typed DataSet provides strongly typed members to access tables and columns by name, instead of using generic collection-based methods. This is beneficial for at least two reasons. First, it improves the overall readability of the code and provides significant help from the Visual Studio 2005 IDE through IntelliSense® and automatic code completion. Second, typed DataSets let you distinguish one table from the next using different objects to render each. Table Employees, for example, will be a different object from table Customers. In this way, type mismatch errors are caught at compile time rather than during execution.

Typed DataSets are still data containers, they're just a bit less generic and have a little more information about the data they contain. They can still hold any data, but you get some specialized members to speed up any work you need to do on a few particular types of data.

Typed DataSets include the same serialization algorithm as DataSets, but because they're derived classes it's easier to extend them further with manually written code to improve the serialization mechanism. For example, your typed DataSet can reimplement the ISerializable interface to reduce or compress the amount of data being moved if you find out that in the particular context in which it operates the XML-based serialization algorithm is too heavy. Speaking of this, let me add a brief remark. As Figure 1 shows, the XML algorithm performance is not so bad when you're moving only a few hundred rows. Sure, each system has its own size and data, but if you realize you're moving thousands of rows across the layers, spend some time making sure you're doing it right. It might be that a proper refactoring would reduce the need to transfer large DataSets from tier to tier too frequently.

A typed DataSet is automatically generated from an XML Schema Definition (XSD) schema file to give DataSet elements friendly names without altering the underlying schema. They have the same advantages of DataSets and they partially remedy the inherent "container" nature of DataSets by adding more specific members oriented to better represent real entities that are active in the domain of the system.

Another advantage of typed DataSets is their support for annotations. Using annotations, you can change the names of contained objects to more meaningful names without changing the underlying schema, making code easier for clients to use. To some extent this is possible with the DataSet, but it comes at the cost of modifying the queries or stored procedures to add T-SQL AS clauses. Annotations build a customized facade atop the resultsets that you get from the database.

Annotations can also be used to easily handle NULL values in DALs powered by the .NET Framework 1.x. Annotations allow you to define the value that a field will return if its actual value is NULL. Note, though, that the problem of dealing with NULLs is more general and can't be entirely avoided or delegated to built-in solutions. Annotations provide an interesting declarative shortcut that neither untyped DataSets nor custom business entities support natively. In custom entity classes, though, dealing with NULL values is a kind of false problem in the sense that it is a problem your classes should solve if they are to provide a good representation of business entities.

Personally, I'm not crazy about typed DataSets, although I recognize they are an improvement over untyped DataSets. The real alternative that I see to DataSets—with costs and benefits that must be carefully evaluated—are custom classes and collections.

Custom Entities and Collections

Orthogonal to the use of generic and polymorphic containers like DataSets is the use of custom objects (custom business entities). Imagine you're building software for an accounting system that manages invoices, customers, orders, and related details. You can represent the list of customers through a DataSet that also includes orders and order details in separate tables connected to each other through in-memory relations. The representation of data is flat; you get to data via a relational API that is generic (Tables and Relations properties) with untyped DataSets and a little more precise with typed DataSets.

When you access data for a customer, you need to have all orders and related details at hand, possibly in the same data structure and available through ad hoc methods and behaviors. You can also code this via custom classes and collections. Figure 2 illustrates the different approaches that are available.

Figure 2 DataSets and Custom Entities
Figure 2 DataSets and Custom Entities

Custom entities provide the means to expose real data in easy-to-access APIs without forcing every data model to fit in the relational model. Like it or not, in the real world all data is rarely entirely relational. By using DataSets, you render data with some approximation, albeit without loss of information.

Custom entities supply strong typing and more compact, faster objects. At the same time, they are abstract enough to require no changes to the DAL if the underlying database schema changes. Being custom classes, they enable you to incorporate information aggregated from multiple sources and represent free-form and hierarchical data. A custom class can be marked as serializable and serialized through any super-optimized algorithm. Of course, you have to write these custom classes yourself (or have a tool generate them for you), whereas the DataSet class already exists.

Make Your Choice

The key feature of custom entity classes is that they add behavior to your data. In doing so, they add expressivity to your code and enable the representation of any complex relationship. The key feature of DataSets is that they are good enough to do virtually everything and have no significant development costs associated with them. DataSets provide excellent support for optimistic concurrency scenarios and include a built-in mechanism for indexing, filtering, and searching tables and creating relations between tables. Moreover, DataViews enable you to create dynamic views of the data stored in a DataSet, a capability that is often used in data-binding applications. Using a DataView, you can expose the data in a table with various sort orders, and you can filter the data by row state or based on filter expressions. Another important difference between DataSets and custom classes is that a DataSet is already a special flavor of a collection class. For custom business entities you must implement a whole slew of collection interfaces in order to provide for effective containment and data-binding capabilities.

In my opinion, using custom business objects results in a far more elegant and neat solution where data is perfectly modeled on the domain of the problem. Maintenance and extensions are greatly facilitated, readability is assured, and technical documentation is easier. These benefits, though, frequently come at a significant development cost. And the cost is so high that many developers and architects using .NET sometimes don't even consider it.

Using custom business entities means that you write the following: collection classes to hold data (OrderCollection), classes to represent business entities (OrderInfo), factory classes to instantiate objects, and helper classes to take care of data access and persistence and to populate the facade classes exposed to the system. If you need data serialization across tiers, you have to ensure your classes are properly serializable. If you need to support aggregation of data sources or multiple versions of data, you need to provide that yourself, whereas DataSet provides aggregation capabilities through its merge functionality. If you need optimistic concurrency and batch update capabilities, you must implement that yourself. Unlike implementing ISerializable, creating a mechanism for concurrency is not trivial. With custom classes, you don't have an engine like ADO.NET relations to create logical links between tables. You also have to explicitly add support for the design-time scenarios already supported by a DataSet (for more info, see Paul Ballard's article in this issue). Mapping custom collections and entities to a database can also be a complicated process that can require a significant amount of code, although custom tools can frequently provide autogenerated classes to aid in this process.

However, data relations is not necessary in all cases. Relations are generally used to provide child trees of data—for example, details of an order or all invoices for a customer. With custom classes, you usually have free-form code that can easily incorporate hierarchical data (see Figure 2). Relations are used to implement referential integrity in memory and cascade changes through related objects. This feature, if needed, must be coded manually.

You'll need to be ready to write custom classes that represent entities and collection classes to group entities. In the .NET Framework 1.x, to write collections you can take advantage of the CollectionBase class or implement IList directly. In the .NET Framework 2.0, generics greatly simplify things. A fully functional collection is not enough, though. If you want to bind custom entities to the presentation layer through data binding, you need to implement more interfaces—in particular, IBindingList and ITypedList.

When to Use Which

Both DataSets and custom classes don't limit what you can do in any way, and both can be used to accomplish the same aims. That said, DataSets are fantastic tools for prototyping applications and represent excellent solutions for building systems in a kind of emergency—a limited budget, an approaching deadline, or a short application lifetime. For relatively simple applications, custom entities add a perhaps unnecessary level of complexity. In this case, I suggest that you seriously consider using DataSets.

In the economy of a large, durable, complex enterprise system that takes several months to complete, the cost of architecting and implementing a bunch of collections classes is relatively minimal and is incurred only once. The advantages in terms of performance, expressivity, readability, and ease of maintenance largely repay the investment. You are not bound to a tabular rendering of data. Business rules and custom business entities can't always be adapted to look like a collection of tables. In general, you should avoid adapting data to the data container—quite the reverse, I'd say. Finally, using custom classes makes for easier unit testing because classes and logic are more strictly related than with DataSets. In Figure 3, you find a synoptic table with DataSets, typed DataSets, and custom entities compared by several factors.

Figure 3 DataSets, Typed DataSets, and Custom Business Entities (Close Figure 3)

 | DataSet | Typed DataSet | Custom Entities
Built-in support for concurrency | Yes | Yes | To be added
Data Relationship | Yes | Yes | No
Serialization | Inefficient in .NET Framework 1.x | Same as DataSet, but can be improved | To be added
NULL values | No | Yes | To be added
Schema abstraction | Yes | Yes | Yes
Strong typing | No | Yes | Yes
Support for hierarchical data | Yes, but through a relational API | Yes, but through a relational API | Yes
Free-form data | No | No | Yes
Custom behavior | No | To be added | Yes
Ease of development | Yes | Yes | No, but can be improved through custom wizards and code generation
.NET data binding | Yes | Yes | To be added; requires the implementation of several additional interfaces
Interfacing with Web services | Costly, unless knowledge of the object is assumed on the client | Schema information is more precise and can be handled by the client | Yes
XML integration | Yes | Yes | To be added
Expression language | Yes | Yes | To be added
Data aggregation | Yes | Yes | To be added

In any case, bear in mind that your decision should be based on full awareness of the benefits, implications, and repercussions. If you go for custom classes, take a look at some commonly used enterprise design patterns. Figure 4 details a few of them.

Figure 4 Design Patterns for Building a DAL (Close Figure 4)

Pattern | Description
Active Record | The entity object stores its own data as well as any available methods. Clients get an instance of the object and work with it as needed.
Data Mapper | The entity object contains only its own data. A neat separation exists between data and behavior. Behavior is delegated to an array of separate classes specific to the object.
Table Data Gateway | Variation of Data Mapper that delegates the implementation of required behaviors to external, gateway classes not specific to the object. The gateway can take scalar and instance data and serve multiple business objects.

Wrap Up

The DataSet versus custom collection disagreement is an old one that for various reasons has never escalated into an outright war. However, the advent of the .NET Framework 2.0 and generics enables developers to generate and manage collections much more quickly and effectively. For example, implementing IBindingList—which is required for data binding—is as easy as adding a couple of overrides to a generic class. This could push the use of custom entities by making it affordable for more developers and in more scenarios. For this reason, it's more important than ever to learn the underpinnings of DataSets and collections so you can make your choice consciously and wisely. For more information on these topics, take a look at the following blogs: Scott Hanselman's at Returning DataSets from WebServices is the Spawn of Satan and Represents All That Is Truly Evil in the World, Jelle Druyts' at DataSets Are Not Evil, Andrew Conrad's at Nix the DataSet??????, and ObjectSharp at DataSet FAQ.

Send your questions and comments for Dino to cutting@microsoft.com.

Dino Esposito is a Wintellect instructor and consultant based in Italy. Author of Programming ASP.NET and the new book Introducing ASP.NET 2.0 (both from Microsoft Press), he spends most of his time teaching classes on ASP.NET and ADO.NET and speaking at conferences. Get in touch with Dino at cutting@microsoft.com or join the blog at weblogs.asp.net/despos.


Test Run: Test Harness Design Patterns

Test Run
Test Harness Design Patterns
James McCaffrey and James Newkirk

Code download available at: TestRun0508.exe (147KB)
Contents
The Six Basic Lightweight Test Harness Patterns
Flat Test Case Data
Hierarchical Test Case Data
Relational Test Case Data
The TDD Approach with the NUnit Framework
Conclusion

The Microsoft® .NET Framework provides you with many ways to write software test automation. But in conversations with my colleagues I discovered that most engineers tend to use only one or two of the many fundamental test harness design patterns available to them. Most often this is true because many developers and testers simply aren't aware that there are more possibilities.

Furthermore I discovered that there is some confusion and debate about when to use a lightweight test harness and when to use a more sophisticated test framework like the popular NUnit. In this month's column James Newkirk, the original author of NUnit, joins me to explain and demonstrate how to use fundamental lightweight test harness patterns and also show you their relation to the more powerful NUnit test framework.

The best way to show you where the two of us are headed is with three screen shots. Suppose you are developing a .NET-based application for Windows®. The screen shot in Figure 1 shows the fairly simplistic but representative example of a poker game. The poker application references a PokerLib.dll library that has classes to create and manipulate various poker objects. In particular there is a Hand constructor that accepts a string argument like "Ah Kh Qh Jh Th" (ace of hearts through 10 of hearts) and a Hand.GetHandType method that returns an enumerated type with a string representation like RoyalFlush.

Figure 1 System Under Test
Figure 1 System Under Test

Now suppose you want to test the underlying PokerLib.dll methods for functional correctness. Manually testing the library would be time-consuming, inefficient, error-prone, and tedious. You have two better testing strategies. A first alternative to manual testing is to write a lightweight test harness that reads test case input and expected values from external storage, calls methods in the library, and compares the actual result with the expected result. When using this approach, you can employ one of several basic design patterns. Figure 2 shows a screen shot of a test run that uses the simplest of the design patterns. Notice that there are five test cases included in this run; four cases passed and one failed. The second alternative to manual testing is to use a test framework. Figure 3 shows a screen shot of a test run which uses the NUnit framework.

Figure 2 Lightweight Test Harness Run
Figure 2 Lightweight Test Harness Run

In the sections that follow, we will explain fundamental lightweight test harness design patterns, show you a basic NUnit test framework approach, give you guidance on when each technique is most appropriate, and describe how you can adapt each technique to meet your own needs. You'll learn the pros and cons of multiple test design patterns, and this information will be a valuable addition to your developer, tester, and manager skill sets.

Figure 3 NUnit Test Framework Run
Figure 3 NUnit Test Framework Run

The Six Basic Lightweight Test Harness Patterns

It is useful to classify lightweight data-driven test harness design patterns into six categories based on type of test case storage and test case processing model. There are three fundamental types of test case storage: flat file, hierarchical, and relational. Additionally, there are two fundamental processing models: streaming and buffered. This categorization leads to six test harness design patterns, the cross-product of the storage types with the processing models.

Of course you can think of many other possibilities, but these six categories give you a practical way to think about structuring your lightweight test harnesses. Notice that this assumes that the test case storage is external to the test harness code. In general, external test case storage is better than embedding test case data with the harness code because external storage can be edited and shared more easily than embedded data. However, as we'll explain later, the test-driven approach is primarily a developer activity and typically uses embedded test case data which does have certain advantages over external data. Separately, NUnit can be used with external test case storage and can support both streaming and buffered processing models.

Flat Test Case Data

The most rudimentary type of test case data is flat data. The data in Figure 4 is the test case file used to generate the test run shown in Figure 2. Compared with hierarchical data and relational data, flat data is most appropriate when you have simple test case input and expected values, you are not in an XML-dominated environment, and you do not have a large test management structure.

Figure 4 Flat Test Case Data
Figure 4 Flat Test Case Data

At a minimum every test case has an ID, one or more inputs, and one or more expected results. There is nothing profound about how to store test case data. Examples of flat data are text files, Excel worksheets, and individual tables in a database. Examples of hierarchical data stores are XML files and some .ini files. SQL Server™ databases and Access databases are examples of relational data stores when multiple tables are used in conjunction through relationships. Here you can see we're using a simple text file with a test case ID field, a single input field, and a single expected result field—simple and effective. We will discuss the pros and cons of each of the three storage types later in this column.

This pseudocode shows the basic streaming processing model:

open test case data store
loop
  read a test case
  parse id, input, expected
  send input to system under test
  if actual result == expected result
    write pass result to external results file
  else
    write fail result to external results file
  end if
end loop

The code in Figure 5 shows the main loop. The algorithm is implemented in Visual Basic® .NET, but any .NET-targeted language could be used. The complete source code for all examples is available in the code download that accompanies this column.

Figure 5 Streaming Flat Data Design (Close Figure 5)
while ((line = sr.ReadLine()) != null) // main loop
{
  tokens = line.Split(':'); // parse input
  caseid = tokens[0];
  cards = tokens[1].Split(' ');
  expected = tokens[2];

  Hand h = new Hand(cards[0],cards[1],cards[2],cards[3],cards[4]); // test
  actual = h.GetHandType().ToString();
  Console.Write(caseid + " ");
  sw.Write(caseid + " ");
  if (actual == expected) // determine result
  {
    string rv = string.Format("  Pass   {0} = {1}",
      h.ToShortString(), actual);
    Console.WriteLine(rv);
    sw.WriteLine(rv);
  }
  else
  {
    string rv = string.Format(" *FAIL*  actual = {0} expected = {1}",
      actual, expected);
    Console.WriteLine(rv);
    sw.WriteLine(rv);
  }
} //main loop

Notice that we echo test results to the command shell with a Console.WriteLine statement and write test results to an external text file with a call to StreamWriter.WriteLine. In general, it makes sense to save test case results to the same type of storage as your test case data, but this is considered to be more a matter of consistency than a technical issue.

We call the algorithm a streaming model because it resembles the .NET input-output streaming model; there is a continuous stream of test case input and test results. Now let's look at the buffered model. The pseudocode in Figure 6 is what we'll call the buffered processing model.

Figure 6 Buffered Algorithm (Close Figure 6)
open test case data store
loop
  read a test case from external storage
  save test data to in-memory data store
end loop

loop
  read a test case
  parse test case id, input, expected
  send input to system under test
  if actual result == expected result
    write pass result to in-memory data store
  else
    write fail result to in-memory data store
  end if
end loop

loop
  read test result from in-memory store
  write test result to external storage
end loop

With the buffered test harness model we read all test case data into memory before executing any test cases. All test results are saved to an in-memory data store and then emitted to external storage after all test cases have been executed. In other words, test case input and results are buffered through the test system rather than streamed through the system. The code snippet in Figure 7 shows you how we implemented the buffered model using the test case data file that is shown in Figure 4.

Figure 7 Flat Data Buffered Design (Close Figure 7)
// 1. read test case data into memory
ArrayList cases = new ArrayList();
string line;
while ((line = sr.ReadLine()) != null) cases.Add(line);

// 2. main test processing loop
ArrayList results = new ArrayList();
string caseid, expected, actual, result;
string[] tokens, cards;
for (int i = 0; i < cases.Count; ++i)
{
  tokens = cases[i].ToString().Split(':'); // parse input
  caseid = tokens[0];
  cards = tokens[1].Split(' ');
  expected = tokens[2];

  Hand h = new Hand(cards[0],cards[1],cards[2],cards[3],cards[4]);
  actual = h.GetHandType().ToString();
  result = caseid + " " + (actual == expected ?
    "  Pass   " + h.ToShortString() + " = " + actual :
    " *FAIL*  actual = " + actual + " expected = " + expected);
  results.Add(result); // store result into memory
}

// 3. emit results to external storage
for (int i = 0; i < results.Count; ++i)
{
  Console.WriteLine(results[i].ToString());
  sw.WriteLine(results[i]);
}

If you compare the streaming processing model with the buffered model, it's pretty clear that the streaming model is both simpler and shorter. So why would you ever want to use the buffered model? There are two common testing scenarios where you should consider using the buffered processing model instead of the streaming model. First, if the aspect in the system under test involves file input/output, you often want to minimize the test harness file operations. This is especially true if you are monitoring performance. Second, if you need to perform some pre-processing of your test case input or post-processing of your test case results (for example aggregating various test case category results), it's almost always more convenient to have all results in memory where you can process them. The NUnit test framework is very flexible and can use external test case storage primarily with the buffered processing models. However, a complete discussion of how to use NUnit in these ways would require an entire article by itself and is outside the scope of this column.

Hierarchical Test Case Data

Hierarchical test case data, especially XML, has become very common. In this section we will show you the streaming and buffered lightweight test harness processing models when used in conjunction with XML test case data. Compared with flat test case data and relational data, hierarchical XML-based test case data is most appropriate when you have relatively complex test case input or expected results, or you are in an XML-based environment (your development and test effort infrastructure relies heavily on XML technologies). Here is a sample of XML-based test case data that corresponds to the flat file test case data in Figure 4:

<?xml version="1.0" ?>
<TestCases>
    <case caseid="0001">
        <input>Ah Kh Qh Jh Th</input>
        <expected>RoyalFlush</expected>
    </case>
    <case caseid="0002">
        <input>Qh Qs 5h 5c 5d</input>
        <expected>FullHouseFivesOverQueens</expected>
    </case>
    ...
</TestCases>

Because XML is so flexible there are many hierarchical structures we could have chosen. For example, the same test cases could have been stored as follows:

<?xml version="1.0" ?>
<TestCases>
    <case caseid="0001" input="Ah Kh Qh Jh Th" expected="RoyalFlush" />
    <case caseid="0002" input="Qh Qs 5h 5c 5d"
        expected=" FullHouseFivesOverQueens" />
    ...
</TestCases>

Just as with flat test case data, you can use a streaming processing model or a buffered model. In each case the algorithm is the same as shown in the basic streaming processing model algorithm and in the buffered algorithm that was shown in Figure 6. Interestingly though, the XML test case data model implementations are quite different from their flat data counterparts. Figure 8 shows key code from a C#-based streaming model implementation.

Figure 8 XML Data Streaming Design (Close Figure 8)
xtw.WriteStartDocument();
xtw.WriteStartElement("TestResults");

while (!xtr.EOF) // main loop
{
  if (xtr.Name == "TestCases" && !xtr.IsStartElement()) break;

  while (xtr.Name != "case" || !xtr.IsStartElement())
    xtr.Read(); // advance to a <case> element if not there yet

  caseid = xtr.GetAttribute("caseid");
  xtr.Read(); // advance to <input>
  input = xtr.ReadElementString("input"); // advance to <expected>
  expected = xtr.ReadElementString("expected"); // advance to </case>
  xtr.Read(); // advance to next <case> or </TestCases>

  cards = input.Split(' ');
  Hand h = new Hand(cards[0],cards[1],cards[2],cards[3],cards[4]);
  actual = h.GetHandType().ToString();

  xtw.WriteStartElement("result");
  xtw.WriteStartAttribute("caseid", null);
  xtw.WriteString(caseid);
  xtw.WriteEndAttribute();
  xtw.WriteString(actual == expected ?
    "  Pass   " + h.ToShortString() + " = " + actual :
    " *FAIL*  actual = " + actual + " expected = " + expected);
  xtw.WriteEndElement(); // </result>
} // main loop

With a streaming model, we use an XmlTextReader object to read one XML node at a time. But because XML is hierarchical it is a bit tricky to keep track of exactly where we are within the file, especially when the nested becomes more extreme (in this particular example, the data is little more than a flat file, but it could be significantly more complex). We use an XmlTextWriter object to save test results in XML form. Now we'll show you a buffered approach for XML test case data. Figure 9 shows key code from a buffered processing model implementation.

Figure 9 XML Data Buffered Design (Close Figure 9)
// 1. read test case data into memory
XmlSerializer xds = new XmlSerializer(typeof(TestCases));
TestCases tc = (TestCases)xds.Deserialize(sr);

// 2. processing loop
string expected, actual;
string[] cards;
TestResults tr = new TestResults(tc.Items.Length);

for (int i = 0; i < tc.Items.Length; ++i) // test loop
{
  SingleResult res = new SingleResult();
  res.caseid = tc.Items[i].caseid;
  cards = tc.Items[i].input.Split(' ');
  expected = tc.Items[i].expected;

  Hand h = new Hand(cards[0],cards[1],cards[2],cards[3],cards[4]);
  actual = h.GetHandType().ToString();

  res.result = (actual == expected) ? // store results into memory
    "Pass " + h.ToShortString() + " = " + actual :
    "*FAIL* " + "actual = " + actual + " expected = " + expected;
  tr.Items[i] = res;
}

// 3. emit results to external storage
XmlTextWriter xtw = new XmlTextWriter("..\\..\\TestResults.xml",System.Text.Encoding.UTF8);
XmlSerializer xs = new XmlSerializer(typeof(TestResults));
xs.Serialize(xtw,tr);

We use an XmlSerializer object from the System.Xml.Serialization namespace to read the entire XML test case file into memory with a single line of code and also to write the entire XML result file with a single line of code. Of course, this requires us to prepare appropriate collection classes (TestCases and TestResults in the code) to hold the data.

Unlike flat test case data, with XML data the buffered model test harness code tends to be shorter and simpler. So when might you consider using a streaming model in conjunction with XML test case data? Most often you will want to use a streaming model when you have a lot of test cases to deal with. Reading a huge amount of test case data into memory all at once may not always be possible, especially if you are running stress tests under conditions of reduced internal memory.

Relational Test Case Data

In this section we'll describe the streaming and buffered lightweight test harness processing models when used in conjunction with SQL test case data. Compared with flat data and hierarchical data, relational SQL-based test case data is most appropriate when you have a very large number of test cases, or you are in a relatively long product cycle (because you will end up having to store lots of test results), or you are working in a relatively sophisticated development and test infrastructure (because you will have lots of test management tools). Figure 10 shows test case data thas has been stored in a SQL database.

Figure 10 SQL-based Test Case Data
Figure 10 SQL-based Test Case Data

Just as with flat test case data and hierarchical data, you can use a streaming processing model or a buffered model. The basic streaming and buffered models described earlier will be the same.

The streaming model implementation is included in this column's download. If you examine the code you'll see that for a streaming model we like to use a SqlDataReader object and its Read method. For consistency we insert test results into a SQL table rather than save to a text file or XML file. We prefer to use two SQL connections—one to read test case data and one to insert test results. As with all the techniques in this column, there are many alternatives available to you.

The code for the buffered processing model can be downloaded from the MSDN Magazine Web site. Briefly, we connect to the test case database, fill a DataSet with all the test case data, iterate through each case, test, store all results into a second DataSet, and finally emit all results to a SQL table.

Using relational test case data in conjunction with ADO.NET provides you with many options. Assuming memory limits allow, we typically prefer to read all test case data into a DataSet object. Because all the test case data is in a single table, we could also have avoided the relatively expensive overhead of a DataSet by just using a DataTable object. However in situations where your test case data is contained in multiple tables, reading into a DataSet gives you an easy way to manipulate test case data using a DataRelation object. Similarly, to hold test case results we create a second DataSet object and a DataTable object. After running all the test cases we open a connection to the database that holds the results table (in this example it's the same database that holds the test case data) and write results using the SqlDataAdapter.Update method.

Recall that when using flat test case data, a streaming processing model tends to be simpler than a buffered model, but that when using hierarchical XML data, the opposite is usually true. When using test case data stored in a single table in SQL Server, a streaming processing model tends to be simpler than a buffered model and the technique of choice. When test case data spans multiple tables, you'll likely want to use a buffered processing model.

The TDD Approach with the NUnit Framework

In the previous sections you've seen six closely related lightweight test harness design patterns. A significantly different but complementary approach is to use an existing test framework. The best known framework for use in a .NET environment is the elegant NUnit framework as shown in Figure 3. See the MSDN®Magazine article by James Newkirk and Will Stott at Test-Driven C#: Improve the Design and Flexibility of Your Project with Extreme Programming Techniques for details. The code snippet in Figure 11 shows how you can use NUnit to create a DLL that can be used by NUnit's GUI interface. And the code snippet in Figure 12 shows how you can use NUnit with external XML test case data to create a DLL that can be used by NUnit's command-line interface.

Figure 12 NUnit Approach with External XML Test Cases (Close Figure 12)
[Suite]
public static TestSuite Suite
{
  get
  {
    TestSuite testSuite = new TestSuite("XML Buffered Example");
    using(StreamReader reader = new StreamReader("TestCases.xml"))
    {
      XmlSerializer xds = new XmlSerializer(typeof(TestCases));
      TestCases testCases = (TestCases)xds.Deserialize(reader);

      foreach(Case testCase in testCases.cases)
      {
        string[] cards = testCase.input.Split(' ');
        HandType expectedHandType = (HandType)Enum.Parse(
          typeof(HandType), testCase.expected);
        Hand hand = new Hand(cards[0], cards[1], cards[2],
          cards[3], cards[4]);
        testSuite.Add(new HandTypeFixture(
          testCase.id, expectedHandType, hand.GetHandType()));
      }
    }
    return testSuite;
  }
}
Figure 11 NUnit Approach with Embedded Test Cases (Close Figure 11)
using NUnit.Framework;
using PokerLib;

[TestFixture]
public class HandFixture
{
  [Test]
  public void RoyalFlush()
  {
    Hand hand = new Hand("Ah", "Kh", "Qh", "Jh", "Th");
    Assert.AreEqual(HandType.RoyalFlush, hand.GetHandType());
  }
  ... // other tests here
}

You may be wondering whether it's better to use NUnit or to write a custom test harness. The best answer is that it really depends on your scenarios and environment, but using both test techniques together ensures a thorough test effort. The NUnit framework and lightweight test harnesses are designed for different testing situations. NUnit was specifically designed to perform unit testing in a test-driven development (TDD) environment, and it is a very powerful tool. A lightweight test harness is useful in a wide range of situations, such as when integrated into the build process, and is more traditional than the NUnit framework in the sense that a custom harness assumes a conventional spiral-type software development process (code, test, fix).

A consequence of NUnit's TDD philosophy is that test case data is typically embedded with the code under test. Although embedded test case data cannot easily be shared (for example when you want to test across different system configurations), embedded data has the advantage of being tightly coupled with the code it's designed to test, which makes your test management process easier. Test-driven development with NUnit helps you write code and test it. This is why embedded tests with NUnit are acceptable—you change your tests as you change your code. Now this is not to say that the two test approaches are mutually exclusive; in particular NUnit works nicely in a code-first, test-later environment, and can utilize an external test case data source. And a lightweight test harness can be used in conjunction with a TDD philosophy.

The NUnit framework and custom lightweight test harnesses have different strengths and weaknesses. Some of NUnit's strengths are that it is a solid, stable tool, it is a nearly a de-facto standard because of widespread use, and it has lots of features. The strengths of custom test harnesses are that they are very flexible, allowing you to use internal or external storage in a variety of environments, test for functionality as well as performance, stress, security and other types of testing, and execute sets of individual test cases or multiple state change test scenarios.

Conclusion

Let's briefly summarize. When writing a data-driven lightweight test harness in a .NET environment you can choose one of three types of external test case data storage: flat data (typically a text file), hierarchical data (typically an XML file), or relational data (typically a SQL Server database). Often you will have no choice about the type of data store to use because you will be working in an already existing development environment. Flat data is good for simple test case scenarios, hierarchical data works very well for technically complex test case scenarios, and relational data is best for large test efforts.

When writing a lightweight test harness you can employ either a streaming processing model or you can choose a buffered processing model. A streaming processing model is usually simpler except when used with truly hierarchical or relational data, in which case the opposite is true. A streaming model is useful when you have a very large number of test cases, and a buffered model is most appropriate when you are testing for performance or when you need to process test cases and results. Using a test framework like NUnit is particularly powerful for unit testing when you are employing a TDD philosophy.

With the .NET environment and powerful .NET-based tools like NUnit, it's possible to write great test automation quickly and efficiently. The release of Visual Studio® 2005 will only enhance your ability to write test automation and the Team System version in Visual Studio 2005 will have many NUnit-like features. With software systems increasing in complexity, testing is more important than ever. Knowledge of these test harness patterns as well as of frameworks like NUnit will help you test better and produce better software systems.

Send your questions and comments for James to testrun@microsoft.com.

James McCaffrey works for Volt Information Sciences Inc., where he manages technical training for software engineers working at Microsoft. He has worked on several Microsoft products including Internet Explorer and MSN Search. James can be reached at jmccaffrey@volt.com or v-jammc@microsoft.com.

James Newkirk is the development lead for the Microsoft Platform Architecture Guidance team, building guidance and reusable assets for enterprise customers through the patterns & practices series. He is the coauthor of Test Driven Development in Microsoft .NET (Microsoft Press, March 2004).


Service Station: The Application Connection Designer

Service Station
The Application Connection Designer
Aaron Skonnard

Contents
Application Connection Designer
Designing Service Contracts
Defining Application Connections
Generating the Implementation Code
Implementing Existing Service Contracts
Consuming External Services
State of the Art

In some of my past columns on service orientation and contract-first development, I've mentioned the new suite of designers, formerly codenamed "Whitehorse," that are part of Visual Studio® Team edition for Software Architects Team System. The Distributed System Designers are the first Microsoft design tools that encourage developers to embrace contract-driven service-oriented architecture (SOA) development by offering a productive, user-friendly experience. Although they're comprised of numerous designers, I'll focus on the Application Connection Designer (ACD) because it provides the specific features related to designing service contracts. I won't, however, look at the Logical Datacenter Designer (LDD) or the various features related to deployment. For more information on the Distributed System Designers, check out the Visual Studio 2005 Team System References sidebar.

As you are reading, remember that this column and the various screenshots of the IDE included herein are based on the Beta 2 release of Visual Studio 2005.

Application Connection Designer

The ACD allows you to model designs for distributed systems. The distributed systems may include a variety of application types including Windows®-based applications, Office applications, Web applications, Web services, databases, and generic application types. One of the key features of the ACD is that it makes it possible to design the service contracts for communicating between these different application types without having to be familiar with the intricacies of Web Services Description Language (WSDL) or the particular implementation technology.

Once your applications and service contracts have been designed, the ACD makes it possible to automatically generate skeletons for the modeled applications, including the necessary ASMX code to implement the service contracts as per your design. It also generates the necessary proxy code for any applications designed to use the service. Then your job is reduced to simply implementing the generated WebMethod stubs with the appropriate business logic. And if you need to modify the service contract after generating code, not to worry, the ACD will automatically update the implementation projects keeping them in sync with all modifications to the service contract. This approach is a great example of contract-driven service development.

You can begin using the ACD by opening Visual Studio 2005 and creating a new Distributed System solution. You can do this by selecting New | Project, navigating to Distributed System Solutions. The Distributed System template gives you the ACD design experience.

Select the Distributed System template, specify the solution details and press OK to generate the ACD-based solution. Visual Studio will generate an empty solution that contains a single file with a .ad extension—this file contains the distributed system design information. You can open the ACD to begin designing your solution by opening the .ad file. Figure 1 illustrates what the empty solution looks like after generating the new project, with an open .ad file ready to go.

Figure 1 Empty Distributed System Solution (Close Figure 1)

The idea behind this experience is that you design the structure of the entire distributed system and the relationships between the different applications before writing any code. Once you've finished designing, the ACD will generate the code for you. However, until you use the code generator, there will be only a single .ad file within the solution. During the code generation phase, new implementation projects will be added to the solution automatically.

You design your distributed system on the design surface in the middle area where it says "Drag from the Toolbox to define applications." You can inspect the available application types in the ACD Toolbox shown in Figure 1. You can drag any of these application types to the design surface. You can also define endpoints on certain application types in order to design communication channels between apps. A connection between applications indicates that they'll exchange information according to a service contract.

Designing Service Contracts

You design service contracts within the ACD by designing Web service endpoints associated with application types. Only Web applications or Web services may have Web service endpoints associated with them. In fact, the only difference between a Web application and a Web service in the ACD is that a Web service has at least one Web service endpoint associated with it.

Once you have a Web service application type on the design service, you can give it a name and start specifying its properties. In order to help you visualize how this works, I'm going to walk you through the steps for building a sample distributed system that consists of a Web application that consumes several Web services. Let's start by designing a new Web service for reporting weather information. You can do this by dragging a Web service application type from the toolbox and dropping it on the design surface. Then you can change the name of the service by double-clicking on it and entering your desired name, in this case NewWeatherService, as shown in Figure 2.

Figure 2 Web Service Shape
Figure 2 Web Service Shape

The shape on the design surface represents an ASP.NET Web service application. Note that the designer doesn't currently provide support for generating Web Services Enhancements (WSE) 2.0 implementations. The name you specify for the Web service will become the name of the ASP.NET Web service project once you generate the implementation code. It will also become the name of the virtual directory where the project code is published. Hence, if you want the project and code to go to a specific virtual directory location, make sure you have the virtual directory created ahead of time and use that name in the design.

If you right-click on the shape, you can inspect the various properties exposed by this application type (see Figure 3). For example, you'll want to specify the .NET-targeted language that will later be used to implement the project, and you may want to specify the name of the default class namespace used in the generated code, and so forth. The properties here are all related to the generated implementation code.

Figure 3 Web Service Properties
Figure 3 Web Service Properties

Others will communicate with this Web service through the endpoints it exposes. A given Web service may expose multiple endpoints. In this case, a Web service endpoint is the combination of a service contract and the concrete binding details such as the type of encoding used by the service. You design the former and the ACD generates the latter. The little circle on the left border of the Web service shape (see Figure 2) represents an endpoint exposed by this service. Web service application types may define multiple endpoints if they need to support multiple communication channels.

You can begin designing the Web service endpoint by selecting it on the design surface and opening the Properties window. You'll notice that most of the properties are now related to WSDL details, such as the service and binding names and namespaces (see Figure 4), but it also asks you to specify the names of the Microsoft .NET Framework classes for implementing the ASMX endpoint within the project.

Figure 4 Endpoint Properties
Figure 4 Endpoint Properties

Once you've specified these WSDL properties, you can begin designing the endpoint contract by right-clicking on the endpoint and selecting Define Operations. This causes the Web Service Details window to appear, which gives you a contract designer for modeling the operations exposed by the endpoint (see Figure 5). The Web Service Details window provides a remote procedure call (RPC)-oriented design experience by modeling the endpoint operations like traditional methods with parameter lists, types, and modifiers. I've defined two operations in this example: one named GetForecast and another named GetForecastByDate, which differ only by a single parameter.

Figure 5 Endpoint Details
Figure 5 Endpoint Details

The first thing you'll probably notice is that the types used here are .NET Framework types, not XML Schema Definition (XSD) types. This is a letdown for those who would have preferred a true WSDL-first development experience. You may also notice that I'm only using simple types in this example. You could theoretically specify .NET classes here, but I don't actually have any code in the solution yet—I'm still working in the completely abstract world of service contract design.

At this stage in the design, I think it makes more sense for the ACD to focus on the message design for each operation as opposed to a method-oriented signature. You could imagine an experience where for each operation, the designer lets you drop into the Visual Studio graphical XSD designer to model the input and output messages, or better yet, let you select from libraries of existing XSD types shared across your organization. The XSD types, rather than .NET types, would then show up in this view, and later when the code generator runs, it could generate the appropriate .NET types for each XSD type used in the contract. It's clear from recent design reviews that Microsoft is interested in moving the next version of the product in this direction.

Defining Application Connections

Once you've finished designing the endpoint details as shown in Figure 5, you have successfully designed a service contract without writing any code, or even angle brackets for that matter. Solution Explorer still only contains a single file at this point. Now you can define other applications that use the service by creating connections to the endpoint.

In Figure 6 I've created a new Web application prototype by dragging the Web application shape to the design surface. I've given it a name of WeatherSite and specified its implementation language as C#. Then, I selected the Connection tool from the toolbox and created a connection between the two applications. You do this by clicking on the target endpoint and dragging the line to the consuming application.Visual Studio 2005 Team System References

  • Microsoft Dynamic Systems Initiative
  • Visual Studio 2005 Team System: Designing Distributed Systems for Deployment
  • Bridge the Gap Between Development and Operations with Whitehorse
  • Contract-First Service Development, Part 1
  • Contract-First Service Development, Part 2

Figure 6 Endpoint Connections
Figure 6 Endpoint Connections

Although I've started with a simple one-to-one scenario, the designer supports having multiple applications connecting to a single service, or a single application connecting to multiple services. And I can use a variety of different application types from the toolbox to consume the service. As I continue to build up this sample, you'll be able to see some more interesting scenarios. At this point I've modeled the basic structure of two applications and a communication channel between them based on the NewWeatherEndpoint contract. In the next section I will commit to the initial design with some implementation code.

Generating the Implementation Code

You can have the ACD implement the skeleton projects for the applications in your distributed design by simply right-clicking on the design surface and selecting Implement All Applications. This will generate code for all the applications modeled in your design. When you select this option, a dialog will appear telling you which applications it's about to generate along with the implementation language and project location. (Figure 7 shows how this will look.) Once you press OK, the code-generation begins and you'll see new projects and files added to Solution Explorer. Once the code generation finishes, the application shapes on the design surface will now be accented by drop shadows. The drop shadows provide a visual indication that the applications have been implemented. Given this simple distributed design, your Solution Explorer will contain the files shown in Figure 8 after implementing the projects.

Figure 7 Confirm Application
Figure 7 Confirm Application

There are several things you should notice about the solution at this point. First, note that there is an ASP.NET Web services project providing the implementation for the NewWeatherService application in my design—it contains a single ASMX endpoint that's named NewWeatherEndpoint.asmx. Second, there is an ASP.NET project providing the implementation for the WeatherSite application in my design. It doesn't contain much in the way of ASPX code but it does contain a Web reference to NewWeatherEndpoint.asmx.

Figure 8 Generated Project Files
Figure 8 Generated Project Files

At this point you can build the entire solution without any errors or warnings. Granted, the projects are essentially empty except for the code generated from the service contract. However, if you browse to NewWeatherEndpoint.asmx after building the solution, you should see something similar to Figure 9. If you click on the Service Description link, you'll see the WSDL definition that you designed using the operation designer. Even though you weren't typing in angle brackets all over the place while designing the operations, you were indeed authoring a WSDL contract that became the shared artifact between these two applications.

Then, if you open the NewWeatherEndpoint.asmx.cs file and inspect the source code, you'll notice that it generated the correct WebMethod signatures using the document/literal/bare style of ASMX attributes. The ACD code generator makes the generated .NET Framework classes concrete (as opposed to making them abstract like wsdl.exe /server does). This gives you an executable ASMX endpoint out of the gate, allowing you to browse to the documentation page, view the various operation details, and grab the official WSDL contract even though you haven't implemented the WebMethods yet.

If you inspect the WeatherSite project using the Class View, you'll find a proxy class that was generated from the same contract used to generate the ASMX code. This class is the mirror image of NewWeatherEndpoint.asmx, the proxy consumer for use on the client side. The point is that both projects contain code generated from the same predefined contract. At this stage, developers on both sides of the contract can begin working on their business-related implementation logic in parallel.

Figure 9 NewWeatherEndpoint.asmx
Figure 9 NewWeatherEndpoint.asmx

Regardless of the amount of time you spend during the contract design phase, you're likely to change the contract after generating the implementation code. This is another area in which the ACD becomes extremely powerful. When you need to modify the contract, simply return to your distributed design in the ACD and modify the contract there. After committing the changes, the ACD will automatically update all implementations of the contract, whether it's in a client project (like WeatherSite) or an implementation project (like NewWeatherService). This key feature gives you an iterative development process driven completely by contract design.

Implementing Existing Service Contracts

There's another common service-oriented design scenario that I haven't addressed yet—that of implementing an existing service contract. For example, assume that the contract for the weather service has already been defined by an industry standards body or by another team within your organization and it's your job to provide an implementation that conforms to the predefined contract.

The ACD provides built-in support for this scenario by providing an option to create a Web service endpoint from an existing WSDL definition. You can do this by right-clicking on any Web app or Web service shape found on the design surface and selecting "Create Web Service Endpoints from WSDL". Then the familiar Add Web Reference dialog appears asking you to specify the location of the existing WSDL contract. After you locate the WSDL and press Add Reference, the ACD will generate an endpoint on the app using the information found in the WSDL definition.

If you select the generated endpoint on the designer and view the endpoint details, you'll notice that all of the contract operations have been imported from the WSDL. You shouldn't have to make any changes at this point if your goal is to conform to the predefined contract. In this case you can go straight to the implementation by right-clicking on the Web service shape and selecting Implement—this will only implement the single project for the new Web service application.

Figure 10 Implementing an Existing Service Contract
Figure 10 Implementing an Existing Service Contract

You can connect this Web service with other applications using the Connection tool like before. When you connect endpoints to applications that are already implemented, the ACD will automatically generate the proxy code and add it to the consuming applications. Figure 10 shows what my sample looks like after following these steps—ConformingWeatherEndpoint implements an existing WSDL definition that I provided and the WeatherSite project now contains the proxy code for it. Like before, you would implement the operations in this Web service by opening the generated ASMX code and finishing the stubbed-out WebMethods.

Consuming External Services

The ACD also makes it possible to design connections with external Web services, that is, services whose implementations you don't control. The toolbox contains an External Web service application type for doing this. When you drag it onto the design surface, the Add Web Reference dialog immediately appears asking you for the external Web service's WSDL definition. Once you provide the location and press Add Reference, it adds the new shape to the design surface and it contains an endpoint definition that corresponds to the downloaded WSDL definition.

The shape for external Web services is a different color than the others to provide a visual cue that you don't own the contract or implementation. When you select the generated endpoint and view the endpoint details, you'll also notice that the endpoint contract is read-only. You obviously shouldn't be able to modify the contract since it's controlled by another party. Thus, when you right-click on the shape, you won't see the Implement option anymore.

The only reason you have the External Web service on the design surface is so you can establish connections to it from your other applications. When you do so, the ACD will automatically generate the proxy code in the consuming application based on the WSDL definition associated with the shape. Your job, then, is to author the application logic that consumes the external service—you'll want to use the Class View again to inspect the generated proxy code along with any documentation provided by the external service.

I've done this in the sample and called the new shape ExternalWebService. I pointed the Add Web Reference dialog to a weather service found on www.xmethods.com and called the generated class ExternalWeatherEndpoint. Then I established a connection to it from the WeatherSite application creating the design that is illustrated in Figure 11. Now the WeatherSite application can communicate with three distinct services, two of which are part of this distributed system implementation and one that happens to be controlled by an external party.

Figure 11 Implementing an External Web Service
Figure 11 Implementing an External Web Service

The design shown in Figure 11 is truly an example of service-oriented development and contract-driven development. The design emphasizes the distinction between services and other application types, and the service contracts that drive the system. The contract definitions are what then drive the code-generation process along with continued synchronization support for contract changes and an iterative development process.

State of the Art

The Application Connection Designer that ships with Visual Studio Team System is a great first-step towards a true contract-driven, service-oriented design experience. Using the ACD you can design systems that define new contracts, implement existing contracts, and consume external contracts without writing any code. You can also define contract-based connections between a variety of different application types used in your systems. After designing your system, you can utilize the ACD to generate the skeleton projects around your contracts and continue to keep the code synchronized as the contracts evolve. You should keep your eye on this application designer as it continues to evolve in future releases of Visual Studio.

Send your questions and comments for Aaron to sstation@microsoft.com.

Aaron Skonnard is a co-founder of Pluralsight, a premier Microsoft .NET training provider. Aaron is the author of Pluralsight's Applied Web Services, Applied BizTalk Server 2004, and Introducing Indigo courses. Aaron has spent years developing courses, speaking at conferences, and teaching professional developers. Reach him at pluralsight.com/aaron.


Pure C++: Generic Programming: Template Specialization

Pure C++
Generic Programming: Template Specialization
Stanley B. Lippman

Contents
Partial Template Specialization
Function Template Specialization
Overload Function Resolution

As I discussed in my last column, a param-eterized type that does more than simple storage and retrieval is constrained as to the acceptable types that may be safely bound to it (see Pure C++: CLR Generics Versus C++ Templates. Using generics, these constraints are made explicit through the where clause. In the C++/CLI template facility, you can often work around these constraints by specializing a function or class template—either individual member functions or an entire class. For the sake of illustration, let's add a min function to my tStack class from the previous column. Ordinarily, I would use the generic min algorithm, but that would only help me as a programmer, not as author of an article on template specialization. For your convenience, the tStack class template definition is reproduced in Figure 1.

Figure 1 tStack Declaration (Close Figure 1)
// c++\cli template stack declaration
template <class elemType>
public ref class tStack
{
    vector<elemType> ^m_stack;
    int top;

public:
    tStack();
    tStack( int size );

    elemType pop();
    void push( elemType et );

    // our new function to implement
    elemType tStack<elemType>::min();
};

Figure 2 shows one possible implementation of min. I define min_val, a local variable, to hold the minimum element, and I initialize it to the first element of the container. I then define two iterators, and compare each element against min_val, reassigning it if the value happens to be smaller. Can you identify the implicit constraint? Yep, you got it:

if ( *it < min_val )

Figure 2 min Implementation (Close Figure 2)
template <class elemType>
elemType tStack<elemType>::min()
{
    // note: iterators are value classes...
    vector<elemType>::iterator it = m_stack->begin();
    vector<elemType>::iterator end_it = m_stack->end();

    if ( it == end_it )
        throw gcnew Exception;

    elemType min_val = *it++;
    for ( ; it != end_it; ++it )
        if ( *it < min_val )
            min_val = *it;

    return min_val;
}

The only valid types that can be bound to elemType in terms of the min function, in general, are those that are able to use the built-in less-than operator or that provide their own operator<() instance. If operator<() is not defined for such a type, and an attempt is made to call min on a tStack of items of this type, a compile-time error is issued at the point where the invalid comparison operator is used in min. The System::String class, as it happens, does not have a less-than operator (it uses the CompareTo method of IComparable). If I attempt to invoke min on a tStack instantiated with String, therefore, it breaks down at compile time because the comparison operation fails.

One possible solution that I won't pursue is to define a global operator operator<() that uses CompareTo to compare two values of type String. These global operators are then invoked automatically by tStack<String^>::min():

bool operator<( String^ s1, String^ s2 ) {
     return ( s1->CompareTo( s2 ) < 0 ) ? true : false;
}

The goal, remember, is to prevent the tStack::min member function definition to be instantiated if the user-specified type argument is String. Instead, you want to define your own tStack<String^>::min instance that uses the CompareTo method. You do this by providing a specialized definition for a member of a class template instantiation using an explicit specialization definition. This definition indicates the template name, the template arguments for which the template is specialized, the function parameter list, and the function body. The keyword template is followed by the less than (<) and greater than (>) tokens, followed by the definition of the specialization for the class member (see Figure 3).

Figure 3 Explicit Specialization Definitions (Close Figure 3)
// explicit specialization definitions
template<> String^
tStack<String^>::min( )
{
    vector<String^>::iterator it = m_stack->begin();
    vector<String^>::iterator end_it = m_stack->end();

    if ( it == end_it )
           throw gcnew Exception;

    String^ min_val = *it++;
    for ( ; it != end_it; ++it )
        if ( it->CompareTo( min_val ) < 0 )
            min_val = *it;

    return min_val;
}

Even though the class type tStack<String^> is instantiated from the general class template definition (that is, a String-specific instance is internally generated by the compiler in which each occurrence of the elemType placeholder is replaced with the String type), each object of type tStack<String^> invokes your specialization of the member function min. The tStack::min member function definition is neither expanded nor used for a tStack<String^>.

In some cases the entire class template definition may be inappropriate for use with a particular type. In this case the programmer can provide a definition to specialize the entire class template. The programmer may provide a definition of tStack<String^>:

template <class elemType>
ref class tStack;

// class template specialization
template<> ref class tStack<String^> {
public:
    tStack();
    String^ pop();
    void push( Stack^ et );
    // ...
};

An explicit specialization for a class template can be defined only after the general class template has been declared. If you provide a full class template specialization, you must define each member function or static data member associated with this specialization. The generic member definitions of the class template are never used (nor are they cross-checked) to create the definitions for the members of an explicit specialization. This is because the class template specialization may have a completely different set of class members from the generic template.

When defining a member of a fully specialized class template, such as tStack<String^>, you do not precede its definition with the special template<> marking. Rather, you indicate the specialization by explicitly listing the actual type, as in the following:

// defines the member function min()
// of a class template specialization
String^ tStack<String^>::min() { ... }

Partial Template Specialization

If a class template has more than one template parameter, you can specialize the class template for one or a set of particular parameterized values or types. That is, you might want to provide a template that matches a general template except that some of the template parameters have been replaced by actual types or values. This is possible using partial template specialization. For example, imagine the following Buffer class template:

template <class elemType, int size>
ref class Buffer { ... };
Here is how you might use partial specialization on Buffer to optimally handle buffers with a size of 1KB:
// partial specialization of class template Buffer
template <class elemType>
ref class Buffer<elemType,1024> {
    // Uses special algorithms for 1KB size ...
};
The partial specialization for Buffer only has one type parameter, named elemType, because the value for size is fixed at 1024. The parameter list for a partial template specialization only lists the parameters for which the template arguments are still unknown. However, when you define an instance of the template, you must specify both parameters (unlike the use of a default value for a parameter). In the following example, the class template partial specialization is instantiated with a type argument for elemType that is String:
Buffer<String^,1024> mumble;

However, if you instead write the following line of code, the compiler generates an error flagging the declaration as missing the second parameter:

Buffer<String^> mumble;  // error
Why? What would happen if a developer later introduced a set of specialized Buffers, such as:
template <class elemType>
ref class Buffer<elemType,4096> {};

template <class elemType>
ref class Buffer<elemType,512> {};

Were the second parameter not required in the declaration of mumble in the earlier example, the compiler would have no way to distinguish between the several specializations!

The partial specialization has the same name as the full general template to which it corresponds—Buffer in the example. This raises an interesting question. Notice that the instantiation for Buffer<String^,1024> could be instantiated from the class template definition as well as from the partial specialization. Why is it then that the partial specialization is chosen to instantiate the template? The general rule is that when class template partial specializations are declared, the compiler chooses the template definition that is the most specialized for the instantiation. When no partial specialization can be used, the generic template definition is used.

For example, when Buffer<String^,2048> must be instantiated, this instantiation does not match any of the partial template specializations, so the general template definition is selected.

The definition of a partial specialization is completely disjointed from the definition of the general template. The partial specialization may have a completely different set of members from the generic class template. A class template partial specialization must have its own definitions for its member functions, static data members, and nested types, the same as for a class template specialization. The generic definitions for the members of a class template are never used to instantiate the members of the class template partial specialization.

Partial template specialization of class templates forms the basis of some very sophisticated design idioms within modern C++ usage. If you're interested, you can read more on this usage in Modern C++ Design: Generic Programming and Design Patterns Applied by Andrei Alexandrescu (Addison-Wesley, 2001).

Function Template Specialization

Non-member function templates can also be specialized. Again, in some cases, you may be able to take advantage of some specific knowledge about a type to write a more efficient function than the one that is instantiated from the template. At other times, the general template definition is simply wrong for a type. For example, suppose you have this definition of the function template max:

template <class T>
T max( T t1, T t2 ) {
     return ( t1 > t2 ? t1 : t2 );
}

If the function template is instantiated with a template argument of type System::String, the generated instance fails to compile because, as you saw earlier, the String class does not support either the less-than or greater-than operators. The code in Figure 4 shows how you specialize a function template (again, the general function template must have been declared before you can provide your specialization).

Figure 4 Specializing a Function Template (Close Figure 4)
using namespace System;

// String explicit specialization:
// overrides instantiation from general template definition

// declaration ...
template<> String^ max<String^>( String^ s1, String^ s2 );

// definition ...
template<> String^ max<String^>( String^ s1, String^ s2 )
{
    return ( s1->CompareTo( s2 ) > 0 ? s1 : s2 );
}

The qualification of the function template name with its actual type parameter, max<String^>, can be omitted from the explicit specialization declaration if the template arguments can be deduced from the function parameters. For example, the compiler can deduce that T binds to String in the following specialization of max, and so in this case the language allows the following shorthand notation as a convenience:

// ok: T as String is deduced from parameter types
template<> String^ max( String^, String^ );
Once this explicit specialization is introduced, an invocation such as the following resolves to this specialized instance:
void foo( String^ s1, String^ s2 ) {
     String^ maxString = max( s1, s2 );
     // ...
}

The general function template is not expanded for two arguments of type String. This is the exact behavior that is required.

Whenever you provide an explicit function template specialization, you must always specify both template<> and the function parameter list. For example, the following two declarations of max are illegal and flagged at compile time as such:

// error: invalid specialization declarations

// missing template<>
String^ max<String^>( String^, String^ );

// missing function parameter list
template<> String^ max<String^>;
That said, there is one case in which omitting the template<> portion of a function template specialization is not an error. This is when you declare an ordinary function with a return type and a parameter list that match those of a template instantiation:
// general template definition
template <class T>
T max( T t1, T t2 ) { /* ... */ }

// ok: an ordinary function declaration!
String^ max( String^, String^ );

No doubt a number of you are shaking your heads and thinking, gosh, C++ is really confusing. And you may be wondering why on earth anyone would want to declare an ordinary function that matches a template instantiation instead of declaring an explicit specialization. Well, consider the following example in which things don't quite work the way you would like:

void foo( String^ s1, String^ s2 ) {
     // is the specialized instance resolved?
     String^ maxString = max( "muffy", s2 );

     // ...
}

Under C++/CLI, for overload resolution, the type of a string literal is both const char[n], where n is the length of the literal plus one for the terminating null, and System::String. This means, for example, that, given the set of functions

void f( System::String^ );     // (1)
void f( const char* );         // (2)
void f( std::string );         // (3)
a call such as this one
// resolves to (1) under C++/CLI
f( "bud, not buddy" );
exactly matches (1), whereas under ISO-C++, the resolution would be (2). So, the question is, for the type deduction of a function template, is a string literal also treated as a System::String? And the short answer is, no. (The long answer will be the topic of my next column, which will cover function templates in detail.) The specialized String instance of max therefore is not chosen, and the following call to max
String^ maxString = max( "muffy", s2 ); // error
fails at compile time because the definition of max requires both arguments to be of the same type, T:
template <class T> T max( T t1, T t2 );

What can you do? Turning the template to a two-parameter instance, as in the following redeclaration

template <class T1,class T2> ??? max( T1 t1, T2 t2 );
allows us to compile the invocation of max with muffy and s2, but then breaks down over the greater-than operator, and specifying which parameter type to return.

What I would like is to always force the conversion of a string literal to type String, and this is where the ordinary function declaration comes to the rescue.

Only a limited set of type conversions are applied to convert an argument of a function template instantiation to the type of the corresponding function parameter if the argument participates in the deduction of a template argument. This is also the case if the function template is explicitly specialized. And, as you've seen, a string literal to System::String conversion is not part of that set.

In the case of the pernicious string literal, explicit specializations do not help you bypass the restrictions on type conversions. If you want to allow more than just the set of limited type conversions, you must define an ordinary function instead of a function template specialization. And this is why C++ allows a non-template and template function to be overloaded.

I'm almost finished, but there is one last item to clarify. What does it mean to create the set of max functions that you see in Figure 5? You know that the call

max( 10, 20 );
always resolves to the general template definition, with T deduced to be int. Similarly, you now know that the calls
max( "muffy", s2 );
max( s2, "muffy" );
always resolve to the ordinary function instance in which the literal string is converted to System::String, but then there is the question of which instance the call
max( s2, s2 );
is resolved to in the set of three max functions? To answer this, let's step through the process of resolving an overloaded function.

Figure 5 max Functions (Close Figure 5)
// general template definition

template <class T>
T max( T t1, T t2 ) { /* ... */ }

// our first specialization – fine for two Strings
// but not String, string literal

template<> String^ max<String^>( String^ s1, String^ s2 );

// our ordinary function declaration to support
// a mixture of String, string literal

String^ max( String^, String^ );

Overload Function Resolution

The first step in overload function resolution is just to build the set of candidate functions. The candidate function set consists of the functions that have the same name as the called function and for which a declaration is visible at the point of the call.

The first visible function is the non-template instance. I add that to the candidate list. What about the function template? When a function template is visible, an instantiation of that template is treated as a candidate function if a function can be instantiated using the function call arguments. In my example, the function argument is s2, which is of type String. Template argument deduction binds String to T, and the template instantiation max(String^,String^) is added to the set of candidate functions.

A function template instantiation is entered in the set of candidate functions only if template argument deduction succeeds. However, it is not an error if template argument deduction fails; it just means that no function instantiation is added to the set of candidate functions.

What if template argument deduction succeeds but the template is explicitly specialized for the template arguments deduced, as in my case? Then it is the explicit specialization that is entered in the set of candidate functions in the place of the function that would be instantiated from the generic template definition.

There are, therefore, two candidate functions for the call: the specialized template instantiation and the non-template instance.

// candidate functions
// specialized template ...
template<> String^ max<String^>( String^ s1, String^ s2 );

// non-template instance
String^ max( String^, String^ );

The next step of function overload resolution selects the set of viable functions from the set of candidate functions. For a candidate function to qualify as a viable function, type conversions must exist to convert each actual argument type to the type of the corresponding formal parameter. In the example, both candidate functions are viable.

The last step of resolving an overloaded function consists of ranking the type conversions applied to the arguments to select the best viable function. For the example, both functions appear equally good. Should this, therefore, be treated as an ambiguous call since both are equally viable?

The answer is that the call is not ambiguous. The non-template max is invoked because it's given precedence over the template instantiation. The reasoning behind this is that an explicitly implemented function is in a sense more real than an instance created from a general blueprint.

Surprisingly, in solving the case of the pernicious string literal, I've eliminated any chance of my earlier String specialization from ever being invoked, so I can eliminate it. I only need the general template declaration and the overloaded non-template instance:

// our final overloaded set to support String

template <class T>
T max( T t1, T t2 ) { /* ... */ }

String^ max( String^, String^ );
That may or may not seem complicated, but it sure is powerful—far beyond the scope of what the common language runtime (CLR) generic facility can support in terms of both language integration and flexibility.

Template specialization is a fundamental aspect of C++ template design. It allows for optimal performance, overcoming constraints on individual or families of class types, and for flexible design patterns that have proven invaluable in real-world code. In my next column, I will drill down into C++/CLI support for both template and generic functions.

Send your questions and comments for Stanley to purecpp@microsoft.com.

Stanley B. Lippman is an Architect with the Visual C++ team at Microsoft. He began working on C++ with its inventor, Bjarne Stroustrup, in 1984 at Bell Laboratories. In between, he worked in feature animation at Disney and DreamWorks, was a Distinguished Consultant with JPL, and was a Software Technical Director on Fantasia 2000.


C++ at Work: Dialog Templates, RegexTest

C++ At Work
Dialog Templates, RegexTest
Paul DiLascia

Code download available at: CAtWork0508.exe (279KB)

Q I'd like to create a dialog-based app (the main window is a dialog itself) using MFC and C++. I don't want to use a resource (.rc) file, but I want to create the dialog dynamically in memory. I sifted through MSDN® and found some clues, but no code examples. I know there are DLGTEMPLATE and DLGITEMTEMPLATE structures and the function InitModalIndirect, which probably is used to create the modal dialog, but I have no idea where to start. How can I create a dialog dynamically without using a resource file?

Thomas Zeitlberger

A Creating a dialog on the fly is theoretically straightforward but practically perilous. Just create the proper structures in memory and then call one of the Indirect dialog creation functions: CDialog::CreateIndirect to create a modeless dialog or CDialog::InitModalIndirect to create a modal one (then call DoModal to run it). These correspond to the Win32® API functions ::CreateDialogIndirect and ::DialogBoxIndirect. Whichever method you use, you have to pass a pointer to your dialog template in memory.

Creating the dialog template is conceptually simple: just build and initialize the structures in memory. The details are a bit tricky because the structures are quirky and you have to get everything just right. If you're off by even 1 byte, your app could spiral into neverland with no clue where the problem lies. It's also frustrating to calculate the positions and sizes of child controls since dialogs don't use pixels; they use dialog units, which depend on your dialog's font.

Figure 1 String Dialog
Figure 1 String Dialog

A full exposition of dialog templates that covers every kind of control and dialog is beyond the scope of this column. But I can at least offer a simple example. I wrote a class called CStringDialog that displays a dialog like the one shown in Figure 1 to request a string from the user. To use it, all you have to do is instantiate and then call Init and DoModal:

CStringDialog dlg;
dlg.Init(_T("Hi"), _T("Please enter your name:"));
   if (dlg.DoModal()==IDOK) {
     CString name = dlg.m_str;
     // do something with it...
   }

Figure 3 Dialog Template
Figure 3 Dialog Template

CStringDialog looks and acts like any other CDialog-derived class based on a dialog resource, but it builds its dialog on the fly in memory using templates.

So what exactly is a dialog template, anyway? A dialog template is a memory structure that describes the dialog. What makes the template complex and error-prone is that it's not a simple fixed-length struct like CREATESTRUCT or WNDCLASS. It's a variable-length structure that is comprised of a fixed-length DLGTEMPLATE followed by an array of DLGITEMTEMPLATE structs, one for each item/control in your dialog. Both the DLGTEMPLATE and DLGITEMTEMPLATE have extra variable-length fields that come after the C struct. Figure 2 shows the structures and Figure 3 illustrates the whole layout. Dialog templates resemble something from an assembly language programming manual so now's the time to don your grunge-retardant hazmat suit. I'll take it one step at a time, starting with DLGTEMPLATE.

Figure 2 DlgTemplate.cpp (Close Figure 2)
// one of these for the dialog
typedef struct {
    DWORD style;
    DWORD dwExtendedStyle;
    WORD cdit;
    short x;
    short y;
    short cx;
    short cy;
      // variable-length stuff follows
} DLGTEMPLATE;

// one of these for each dialog item (control)
typedef struct {
    DWORD style;
    DWORD dwExtendedStyle;
    short x;
    short y;
    short cx;
    short cy;
    WORD id;
      // variable-length stuff follows
} DLGITEMTEMPLATE;

Assuming you've allocated a memory block large enough to hold your entire dialog template, the first thing to do is fill out the fields in the DLGTEMPLATE. This is the easy part:

WORD* pTempl = new WORD[1024];
DLGTEMPLATE& dt = *((DLGTEMPLATE*)pTempl);
dt.style = WS_POPUPWINDOW|DS_MODALFRAME|WS_DLGFRAME;
dt.cdit = 3; // # dlg items
dt.x = 100;  // in dlg units
// etc.

The DLGTEMPLATE fields are self-explanatory, so I won't elaborate further. Immediately following the DLGTEMPLATE come three variable-length fields: the menu, dialog class, and caption. Each must fall on a WORD boundary. Each can be a null-terminated Unicode string identifying either the name of a MENU resource, the class name to use for the dialog, or the caption. In addition, the menu and class name can have the special value 0xFFFF followed by a 16-bit ID—either the menu resource ID or the ordinal of a predefined system window class. In almost all cases you should use 0x0000 (null string) for the class, which tells Windows® to use the default dialog class (#32770). Most dialogs don't have a menu, so the menu will also be 0x0000 (null string). In code, it looks like this:

*pTempl++ = 0; // menu (none)
*pTempl++ = 0; // dialog class (use standard)

The caption comes next, a null-terminated Unicode string:

USES_CONVERSION;
LPCWSTR wszText = T2W(_T("My Dialog"));
wcscpy((WCHAR*)pTempl, wszText);
pTempl += wcslen(wszText)+1;
This code works for Unicode or ASCII builds, since with _UNICODE defined, T2W is a no-op. Don't forget to increment the pointer to your template-in-progress (pTempl) to the next WORD after the string. If your dialog has the DS_SETFONT style, there's a fourth field: the 16-bit font size followed by the Unicode font name, for example "Verdana".

Finally, I should point out that there's an extended version of the dialog template, DLGTEMPLATEEX, that lets you specify even more fields like the point size and weight, whether to use italics, the character set, and typeface name. See the documentation for more information; I'll only describe the simple versions here since you can always set the font more easily by calling SetFont in your OnInitDialog handler. (There's also a DLGITEMTEMPLATEEX for items.)

So much for DLGTEMPLATE. Next, the controls. Each control in your dialog is described by an item template (DLGITEMTEMPLATE), which must fall on a DWORD boundary:

pTempl = AlignDWORD(pTempl);
DLGITEMTEMPLATE& it = *((DLGITEMTEMPLATE*)pTempl);
it.x = 0;
it.y = 0;
// etc.

As with DLGTEMPLATE, the DLGITEMTEMPLATE struct is followed by three variable-length fields. In this case they are the class name, text, and creation data. The class name is again either a null-terminated Unicode string specifying the window class name (for example, "SysListView32" or "MyFancyControl"), or the value 0xFFFF followed by one of the special atom codes, that are shown in Figure 4, for standard predefined system controls. For example, the following code shows how to create a static text control:

// class immediately after DLGITEMTEMPLATE
*pTempl++ = 0xFFFF;  // next WORD is atom:
*pTempl++ = 0x0082;  // static control

Figure 4 Atom Codes (Close Figure 4)

Code | Control Type
0x0080 | Button
0x0081 | Edit
0x0082 | Static
0x0083 | List box
0x0084 | Scroll bar
0x0085 | Combo box

Following the class name comes the title. It can be either a Unicode string or the special value 0xFFFF followed by a 16-bit resource ID. You also can use the 0xFFFF + ID format to specify an icon or bitmap for a static control with the SS_ICON or SS_BITMAP style. CStringDialog uses the string form to create its prompt:

USES_CONVERSION;
LPCWSTR wszTest = T2W(_T("My Dialog"));
int maxlen = /* don't overflow! */
wcsncpy((WCHAR*)pTempl, wszText, maxlen); pTempl += wcslen(wszText)+1;

Finally, the "creation data" can be whatever you want. The first WORD is the length of the data, or zero if none. Windows passes a pointer to your data as LPARAM to WM_INITDIALOG (modal dialogs) or WM_CREATE (modeless). I don't recommend using creation data here since it's much easier to add whatever data members you want to your dialog class and initialize them with your dialog's constructor or OnInitDialog handler. But you still have to supply a zero WORD to tell Windows there's no creation data:

*pTempl++ = 0; // no creation data

Once you've completely specified the DLGITEMTEMPLATE, you can do it again for the next control, and the next, and so on for each control in your dialog. Just make sure DLGTEMPLATE::cdit specifies the total number of controls. To simplify the process of building dialog templates (and make it less error prone) I wrote a helper class, CDlgTemplateBuilder. CStringDialog uses it to build its dialog one step at a time:

// in CStringDialog::Init
CDlgTemplateBuilder& dtb = m_dtb;
DLGTEMPLATE* pTempl = dtb.Begin(...);
dtb.AddItem(...);
dtb.AddItem(...);
dtb.AddItem(...);
InitModalIndirect(pTempl, ...);

I've obscured the details to highlight the main idea: you call Begin once, then AddItem for each control. CDlgTemplateBuilder::Begin builds the DLGTEMPLATE and each call to AddItem builds another DLGITEMTEMPLATE. CDlgTemplateBuilder builds the template in its own memory buffer and automatically increments DLGTEMPLATE::cdit (the item count) every time you add another item. CDlgTemplateBuilder has helper functions AlignDWORD and AddText to ensure the proper alignments and perform the proper string conversions. I leave you to download and ponder the source from the MSDN Magazine Web site.

I mentioned that dialogs use dialog units, not pixels. Both DLGTEMPLATE and DLGITEMTEMPLATE have x, y, cx, and cy members to specify the position and size of the dialog or item. These values are in dialog units. Each horizontal dialog unit is one-quarter of a base unit, and each vertical unit is one-eighth of a base unit. A base unit is the average width and height of a character in the dialog and depends on the dialog's font. If this seems like a real pain, it is, but in fairness the idea is laudable: dialog units make your dialog's appearance independent of its font. So whether you use a large font or a small one, the relative positions of all the controls are the same and everything should look correct. Windows has a special function called MapDialogRect to convert dialog units to pixels; amazingly, there's no function to convert the other way, which is what you need to build a template—but you can use the following formulas:

CSize base = ::GetDialogBaseUnits();
xDlg = MulDiv(xPixel, 4, base.cx);
yDlg = MulDiv(yPixel, 8, base.cy);

For CStringDialog, I was too lazy to do all this. Instead, I experimented to find the right values to get a dialog like the one shown in Figure 1. A more sophisticated implementation would check the length of the prompt or allow the caller to specify the dimensions. If dealing with dialog units makes your brain ache, you can create your controls using zeroes for size and position, then implement an OnSize handler that moves them to their proper pixel places. Your dialog may have to post itself a WM_SIZE message from OnInitDialog to make sure the controls are positioned properly when the dialog first starts.

Finally, how did I get CStringDialog to display the question mark in Figure 1? CStringDialog::Init lets you specify the icon for your prompt. The default is IDI_QUESTION. But IDI_QUESTION is a built-in system icon, not an icon from the application's resource file. If you specify a resource ID in your dialog template, Windows expects it to be in the resource file. So how did I get Windows to use the system icon instead?

By fooling it, of course. CStringDialog checks the icon resource ID for a value greater than IDI_APPLICATION, the first system icon ID. If the icon ID is in the system ID range, CStringDialog loads it by calling ::LoadIcon with hInstance set to NULL (for system icons) and saves the HICON loaded in a data member m_hIcon. CStringDialog then constructs the dialog template using the 0xFFFF + nResID format with nResID=0. This causes Windows to create a static icon with no actual icon. CStringDialog then sets the icon in its OnInitDialog handler:

// in CStringDialog::OnInitDialog()
if (m_hIcon) {
  CStatic* pStatic = (CStatic*)GetDlgItem(IDICON);
  pStatic->SetIcon(m_hIcon);
}

The upshot is that you can pass any of the IDI_XXX icon IDs to CStringDialog::Init. You can still use your own icon as long as its ID is below IDI_APPLICATION = 32512. Download the source for details.

Q It's quite a coincidence that you should write about regex DDV validation (see the April 2005 column) since I had to write the same thing recently. Why would you wrap a .NET library and add that dependency (and all the baggage of a wrapper library) when there's a compact and free regex library included with Visual Studio® .NET that becomes available to your MFC app by including a single header file, atlrx.h? It's not 100 percent standard syntax, but I'd much rather live with that than add a .NET Framework dependency.

Gil Rosin

A Well lock me in a straitjacket and spray me with silly string! I didn't even know the Active Template Library (ATL) had a regex class. Well, it just goes to show Windows has more stuff than even a supposed guru can ever possibly hope to know. Indeed, you're right, ATL does provide a regex implementation!

But first, let me correct some impressions about the .NET Framework. I know a lot of you are reluctant to add such a dependency to your apps because you fear code bloat, and I was reluctant too, at first. But using the .NET Framework may not be as bad as you think. While managed apps definitely take a performance hit at startup, believe it or not, once the Framework is loaded, Microsoft® intermediate language (MSIL) code can run even faster than native EXEs. That's because the just-in-time (JIT) compiler is capable of some really hairy performance optimizations. And while applications targeting older versions of Windows like Windows 98 and Windows NT® can't assume the Framework exists, forcing you to install it yourself (see "Using Visual Studio .NET 2003 to Redistribute the .NET Framework" at Using Visual Studio .NET 2003 to Redistribute the .NET Framework, or google "dotnetfx.exe"), newer and future versions of Windows come with the Framework pre-installed. As the .NET Framework becomes more ubiquitous, and as its performance continues to improve, the extra cost (both performance and installation) of calling the Framework should shrink to the vanishing point.

As for my wrapper library, the "baggage" is really just a hair-thin layer of syntactic sugar designed to make the wrappers compile. The most they add is one extra function call since each wrapper object is no more than a handle to the managed object. As I pointed out in my April article (see "Wrappers: Use Our ManWrap Library to Get the Best of .NET in Native C++ Code"), the function call is insignificant for most applications. Also, I chose regex only as an example; my main purpose was to create a general mechanism to wrap any Framework class. And finally, you only need wrappers if you want to stick with an older version of the complier that doesn't support /clr, or you want to avoid it for some reason. Otherwise, toss the wrappers and call the Framework directly through the managed extensions.

Now that I've cleared that up, I must confess I was quite excited to learn the Active Template Library has a regex class, even though I was embarrassed I didn't already know. The first thing I did when I got your e-mail was port the test programs from the ManWrap article to use ATL instead of the .NET library. I wanted to see how easy it would be. I encountered some minor snafus, but overall the process was fairly straightforward.

The Active Template Library implementation of regex is primitive compared to the .NET Framework version, but for many purposes, it does the job fine. ATL uses two template classes: CAtlRegExp, which holds a regular expression; and CAtlREMatchContext, which holds matches. These templates are parameterized by another class that describes the character traits (for example, ASCII, WCHAR, or multibyte). In practice you can ignore this since the ATL templates provide default character traits CAtlRECharTraits based on your settings for _UNICODE:

// in atlrx.h
#ifndef _UNICODE
typedef CAtlRECharTraitsA CAtlRECharTraits;
#else
typedef CAtlRECharTraitsW CAtlRECharTraits;
#endif
template <class CharTraits=CAtlRECharTraits>
class CAtlRegExp; // forward declaration

In effect, all the ATL regex stuff uses TCHARs by default. So to create a regular expression in ATL, you'd write:

CAtlRegExp<> re;
re.Parse("a+b+");
This parses the regular expression into internal structures, so you can then call Match with a CAtlREMatchContext to get the matches, as shown here:
CAtlREMatchContext<> mc;
re.Match("aaabbx", &mc);

CAtlREMatchContext is a little primitive, at least compared to the Framework regex class and other more full-blown implementations. It has a member m_Match that's a MatchGroup, which holds the start and end of the match:

struct MatchGroup {
  const RECHAR *szStart;
  const RECHAR *szEnd;
};
Here RECHAR is whatever character type is defined in the character traits; in practice it's the same as TCHAR if you use the default traits. CAtlREMatchContext can also find matching subgroups within an input string. After calling Match, mc.m_uNumGroups holds the number of matching subgroups and you can call GetMatch(i, ...) to get the ith subgroup match. One of the weird things about ATL regular expressions is that they use curly braces to denote groups, not the standard parentheses. For example:
CAtlRegExp<> re;
re.Parse("{a+}{b+}");
re.Match("aaabbx", &mc);
This would find a single match ("aaabb") with two groups ("aaa" and "bb"). ATL will match normal paren groups, but you can't find the individual sub-matches unless you use curly braces.

CAtlRegExp and CAtlREMatchContext do the job, but they're a little cumbersome to use. For example, to find all the matches, you have to repeatedly call Match with the szEnd pointer from the previous match as the start of the next input string. It's not rocket science, but why should you have to keep track of state when you want to quickly find all the matches? So I encapsulated everything in a single class, CRegex, that makes programming easier. Figure 5 shows the main header file and Figure 6 shows my RegexTest program, from the April ManWrap article, ported to ATL. As with the original program, you enter a regular expression and input string, and RegexTest displays the matches and groups. It uses CRegex to iterate the matches like so:

CRegex re(/* regex */);
re.SetInput(/* input */);
while (re.NextMatch()) {
  int offset=0;
  CString match = re.GetMatch(&offset);
  ...
}

Figure 5 Regex.h (Close Figure 5)
////////////////////////////////////////////////////////////////
// MSDN Magazine — August 2005
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual Studio .NET 2003 (V7.1) on Windows XP. Tab size=3.
//
#pragma once
#include <atlrx.h>            // ATL regex
using namespace std;

//////////////////
// CRegex makes ATL regular expressions a little more usable.
//
class CRegex : public CAtlRegExp<> {
protected:
     CString m_re;              // the regular expression
    BOOL m_bCaseSensitive;      // case sensitive?
    CAtlREMatchContext<> m_mc;  // internal ATL match context
    LPCTSTR m_szIn;             // original input string
    LPCTSTR m_szNext;           // next character to search

    // helper to extract string from ATL MatchGroup
    CString GetMGString(const CAtlREMatchContext<>::MatchGroup& mg);

public:
    REParseError m_err;         // current ATL parse error, if any

    // helper function to get error name from error code
    static LPCTSTR GetErrorName(REParseError err);

    CRegex() : m_err(REPARSE_ERROR_OK) { }
    CRegex(LPCTSTR szRE, BOOL bCaseSensitive=TRUE) {
        m_err = Parse(szRE, bCaseSensitive);
    }
    CRegex(const CRegex& r) { *this = r; }
    ~CRegex() { }

    // only copy Regex and case flag, not dynamic state—then reparse
    const CRegex& operator= (const CRegex& r) {
        m_re = r.m_re;
        m_bCaseSensitive = r.m_bCaseSensitive;
        if (!m_re.IsEmpty())
            m_err = Parse(m_re, m_bCaseSensitive);
        return *this;
    }

    // convert to string: return RE
    operator LPCTSTR() const { return m_re; }

    // Parse RE: reset internal state and return error code.
    REParseError Parse(LPCTSTR szRE, BOOL bCaseSensitive=TRUE) {
        m_re = szRE;
        m_szIn = m_szNext = NULL;
        return CAtlRegExp<>::Parse(szRE, bCaseSensitive);
    }

    // Set input string. Use this with NextMatch to find all matches.
    void SetInput(LPCTSTR szIn) {
        m_szIn = m_szNext = szIn;
    }

    // Find next match after calling SetInput
    BOOL NextMatch() {
        return CAtlRegExp<>::Match(m_szNext, &m_mc, &m_szNext);
    }

    // Find a single match in input string.
    BOOL Match(LPCTSTR szIn, LPCTSTR* ppszEnd=NULL) {
        if (szIn==NULL || *szIn==0)
            return FALSE;
        SetInput(szIn);
        return CAtlRegExp<>::Match(szIn, &m_mc, ppszEnd);
    }

    // Get current match; optional arg returns offset in input string.
    CString GetMatch(int* pOffset=NULL) const {
        if (pOffset)
            *pOffset = (int)(m_mc.m_Match.szStart - m_szIn);
        return GetMGString(m_mc.m_Match);
    }

    // Get number of groups. In ATL syntax, groups are marked with {}.
    UINT GetNumGroups() const { return m_mc.m_uNumGroups; }

    // Get nth match group. Optional arg returns offset into input string.
    CString GetGroup(int nIndex, int* pOffset=NULL) const {
        CAtlREMatchContext<>::MatchGroup mg;
        const_cast<CRegex*>(this)->m_mc.GetMatch(nIndex, &mg);
        if (pOffset)
            *pOffset = (int)(mg.szStart - m_szIn);
        return GetMGString(mg);
    }
};

The CRegex constructor calls Parse automatically (you should check m_err for errors), and if you want the individual group matches, you can call CRegex::GetNumGroups to get the total number of groups for the current match and CRegex::GetGroup to get each group match. Figure 7 shows these details. Internally, CRegex has its own CAtlRegExp and CAtlREMatchContext objects, and keeps track of where it is in the input string—so you don't have to.

Figure 7 FormatResults.cpp (Close Figure 7)
CString FormatResults(CString sRegex, CString sInput)
{
    if (sRegex.IsEmpty())
        return errbeep(_T("Please enter a regular expression!"));
    if (sInput.IsEmpty())
        return errbeep(_T("Please enter an input string!"));

    CString result;
    CString temp;

    CRegex re(sRegex);
    if (re.m_err!=REPARSE_ERROR_OK) {
        result.Format(_T("Oops! Error parsing regex: %s\n"),
            re.GetErrorName());
        MessageBeep(0);
        return result;
    }

    LPCTSTR szInput = sInput;
    int count=0;
    re.SetInput(szInput);
    while (re.NextMatch()) {
        int offset=0;
        CString match = re.GetMatch(&offset);
        temp.Format(_T("Match at %d: %s\n"), offset, match);
        result += temp;

        temp.Format(_T(" Number of groups: %d\n"), re.GetNumGroups());
        result += temp;

        UINT nGroups = re.GetNumGroups();
        for (UINT m=0; m<nGroups; m++) {
            CString group = re.GetGroup(m, &offset);
            temp.Format(_T(" Group %d at %d: %s\n"), m, offset, group);
            result += temp;
        }
        count++;
    }
    if (count<=0)
        result.Format(_T("No match!"));

    return result;
}
Figure 6 RegexTest in Action
Figure 6 RegexTest in Action

I also added a few other useful features. The Active Template Library has no regex replace function, but it wasn't hard to add one. As with the Framework regex class, CRegex::Replace comes in normal and static flavors, so you can call it with or without a CRegex object. For example, the main dialog in RegexTest uses the static version to convert linefeeds (\n) to CRLFs ("\r\n"), like so:

static CString LF2CRLF(LPCTSTR lpsz)
{
  return CRegex::Replace(lpsz,_T("\n"),_T("\r\n"),TRUE);
}

What could be easier? Without CRegex, this would be dozens of lines of code using CAtlRegExp directly. It just goes to show how effective you can be when you take the time to build higher layers of abstraction on top of the basic building blocks the Redmondtonians provide. This is the essence of layered design. I even added a dynamic Replace function similar to the one in the .NET Framework so you can use a callback function to implement a dynamic replace algorithm like the one in my word-scrambler program (WordMess) from the April ManWrap article. Finally, I added Split so you can split a string into an array of substrings using a regular expression as the separator. For example:

vector<CString> strs = CRegex::Split("one,two,three",",");
This returns a vector of three substrings: "one", "two", and "three". Implementing Split is mostly a matter of string manipulation, but once you've written the code, it's yours to use for all eternity. I leave you to download and ponder the source for details.

If you decide to use ATL regular expressions or my CRegex encapsulation of them, be careful: the ATL version of regular expressions is a simplified, nonstandard implementation. I've already mentioned the use of curly braces instead of parens. The ATL also uses nonstandard meta characters (for example, \b instead of \s for whitespace) and you can't use meta characters within ranges ([\a\d] won't work). See Figure 8 for a list of special characters. Read the documentation carefully and watch out for bugs. Complex regular expressions don't always work, and you need #pragma to suppress a couple of level 4 compiler warnings (see the code for details). But if all you need is some basic pattern-matching to validate user input, ATL can get the job done.

Figure 8 CAtlRegExp Special Characters (Close Figure 8)

Abbrev. | Matches
\a | Any alphanumeric character: ([a-zA-Z0-9])
\b | White space (blank): ([ \\t])
\c | Any alphabetic character: ([a-zA-Z])
\d | Any decimal digit: ([0-9])
\h | Any hexadecimal digit: ([0-9a-fA-F])
\n | Newline: (\r|(\r?\n))
\q | A quoted string: (\"[^\"]*\")|(\'[^\']*\')
\w | A simple word: ([a-zA-Z]+)
\z | An integer: ([0-9]+)

Before parting, I should point out to C++ fans that regular expressions are slated to become part of the Standard Template Library. The implementation will most likely be based on the current boost implementation available from boost.org.

Happy programming!

Send your questions and comments for Paul to cppqa@microsoft.com.

Paul DiLascia is a freelance software consultant and Web/UI designer-at-large. He is the author of Windows++: Writing Reusable Windows Code in C++ (Addison-Wesley, 1992). In his spare time, Paul develops PixieLib, an MFC class library available from his Web site, www.dilascia.com.


{ End Bracket }: Improving Managed DirectX Performance

{ End Bracket }
Improving Managed DirectX Performance
Tom Miller

It seems that at least twice a week, I am asked about poor performance in Managed DirectX®. This frequency is actually a big improvement over the 5-10 times a week I was asked a few years ago when the technology first came out. Many of the people who are posing the questions begin with the basic assumption that managed code is slow, and therefore assume that Managed DirectX must also be slow.

Many different types of developers hold this misconception, from the hardcore game developer to programmers within my own DirectX team. In one case, a co-worker was writing a "proof of concept" prototype using Managed DirectX. He was having performance problems, and had concluded it was simply because managed code was slow. I offered to look at it more closely.

I noticed that it indeed ran very slowly, so much so in fact that I first thought it was written that way intentionally. After a review of the code, though, it became clear that this wasn't the case. This situation spawned a design review of the Managed DirectX libraries, resulting in changes to the API. This was my first look at an actual customer scenario, how a real customer might use the API, and it was quite an eye opener.

The biggest performance problem was the complete lack of resource buffers. These buffers are used to store vertex and index data and are optimized to send data to the graphics card efficiently. This developer's application was not using buffers at all, but was instead passing in the entire set of data each and every time the application wanted to draw it. The developer was unaware that this would cause the same data to be copied to the graphics card on each call, even if it hadn't changed. Fixing this class of issues resolved a large part of the application's performance problems.

These draw calls were also not batched, and the application was using essentially one draw call per object. Having thousands upon thousands of draw calls every frame can really hurt your performance. Given that this prototype wasn't intended to perform in real time, I decided to leave the number of draw calls alone.

Profiling the application after these fixes resulted in an extraordinary number of garbage collections happening during the running of the application. Generation 1 and 2 collections were happening as well, and that's something you never want to see in a high-performance 3D application. In looking through the code, I noticed that in virtually every case where an object was needed (such as a device), rather than using a cached version of the object, the developer had simply used the property on a child object to retrieve its parent (for example, VertexBuffer.Device). The problem here was twofold. First, this property was actually returning an entirely new object every time it was accessed by the application, and second, because of the event-hooking mechanism built into Managed DirectX, it was also creating three other miscellaneous objects every time an event was raised.

This type of code pattern was repeated literally everywhere throughout the API. It was causing a minimum of 2,000 extra objects to be created every frame, increasing memory size, causing more and more garbage collections. And even worse, the vast majority of these objects were never being collected at all, despite the numerous garbage collections. My job now was to find out why.

It turns out the culprit was the fancy event handling and hooking that Managed DirectX utilizes. Every resource a device was creating would hook events on the device itself (at the very least the "disposing" event). These event hooks would create a hard link to the child object, keeping it alive regardless of whether it was now out of scope. That event hook ensured that the object could never be collected until the device itself was disposed.

Armed with these two new important facts, I commenced a design review of the Managed DirectX API. Properties that returned other objects now had their values cached internally to remove the need for the application itself to maintain this list. The event-hooking mechanism was changed to allow it to be disabled for applications that wanted more control over its objects' lifetimes. With these changes in place, the application now performed well above expectations.

Nowadays the number of times I'm asked about performance issues is steadily diminishing. It seems developers are realizing that you can get both high performance and increased productivity using Managed DirectX. When they see the Visual Studio® 2005 version that will be released soon, I can only imagine more people will be convinced.

Tom Miller is a developer in the DirectX group at Microsoft, and is the designer and developer of the Managed DirectX libraries, as well as the author of two books on the subject. You can view his blog at blogs.msdn.com/tmiller.