Sunday, February 25, 2007

Navigation Controller Pattern

When people use the term "website navigation", they usually refer to one of the two different things. First is a set of hyperlinks displayed on a web page that users can click in order to get to a specific part of the website. These links may be presented as a drop-down menu, tree-view, or a bread crumb trail. Such navigation controls are easy to incorporate, especially with ASP.Net 2.0 where they all bind to a common data source.

Another meaning of the term is related to page flow: a sequence of pages user needs to go through in order to accomplish some process. Ordering a book from Amazon.com is a simple example: from the shopping cart screen, user has to go to the shipping details page, then billing details, review order summary, order receipt, and, finally, suggested items list. An example of a complex flow is creating a will on LegalZoom.com. In both cases, entire sequence is controlled by the application -- user cannot bypass or reorder steps. Thus, page flow is part of the application's business logic while navigation controls are part of its user interface.

Traditionally, page flow is implemented either in the code-behind class or page presenter (if the application follows the MVP pattern) as a sprinkle of Response.Redirect() calls. Although this approach is straightforward for developers, it has an important limitation: page flow logic is inseparable from the rest of the application logic. Unless the system is fully documented (and we know how often that happens, right?), it is impossible to answer a question "How many pages lead to Foo.aspx" without scanning the source code. Consequently, application becomes difficult to maintain: simple business request to change default page flow becomes a challenge for a developer, especially if he or she isn't intricately familiar with the system, and requires extensive regression testing.

Since I mentioned the subject of technical documentation, one reason it’s so unpopular is because any documentation almost immediately becomes outdated. Imagine how much easier it would have been if we had a “live” system diagram. But that is a subject for a different post…

Navigation Controller design pattern provides a better alternative. As it name implies, entire page flow navigation logic is abstracted to a new class. At the core of navigation controller are two types of page transitions: default and named. Default transition will transfer user to the next web page within the flow, while named transition represents an alternative navigation route. For instance, a page that displays credit card details will transition by default to the order summary page:

IPageFlow controller = this.NavigationController;
controller.Next();

However, if user decided to add new credit card info, control should be redirected to that page instead:

IPageFlow controller = this.NavigationController;
controller.Navigate(TransitionNames.AddNewCreditCard);

Note that the string Navigate method accepts isn’t the URL of the page, but a generic value that represents the transition. The logic within navigation controller maps transition name to actual page URL.

Essentially, navigation controller is a state machine where each state is associated with a particular web page. As all state machines, it needs to have a start and end states, and maintain current state information. From the web application point of view, every user session requires a dedicated instance of the controller class.

As we know, nothing prevents a user from typing the URL directly in their browser window or clicking “Back” button. Both these actions can potentially break the intended page flow, so another function of navigation controller is to “keep” user in:

protected override void OnLoad(EventArgs e)
{
IPageFlow controller = this.NavigationController;
controller.RedirectToCurrentPage();
base.OnLoad(e);
}

Microsoft Patterns and Practices group recently released initial version of its Web Client Software Factory. The factory includes, among other things, Page Flow Application Block, which is a versatile navigation controller. As all PnP application blocks, it is build using provider model, meaning that there may be different concrete implementations of the controller. The team actually chose Windows Workflow Foundation (WF) for their implementation. In WF, page flow is represented by a state machine workflow and can be edited in the visual designer inside VS 2005. Because WF has its own persistence model, page flow instances need not be stored in user session state and can be paused and resumed as necessary. For more information, please visit Web Client Software Factory wiki on CodePlex.

Monday, February 19, 2007

Design For Operations (Part II)

Last time I made a mistake by including words "Part I" in the title of my post. The plan was to write part II right away, and of course, it took me two months to get to it. I bet if I didn't call it "Part I", I would have posted this essay before Christmas ;-)

So, my goal is to make the application more accessible to the people who actually have to maintain production servers. WMI is great for this particular purpose, because it is a standard mechanism for accessing many system components at run-time: disk drives, Windows services, message queues, etc.

1. Publishing to WMI

If I design my application as a WMI provider, it will be able to publish information to WMI infrastructure built into Windows system. Application status information is represented by instances and events. Windows WMI infrastructure will ensure that any client that has proper permissions can query this data, subscribe to events, etc. All I need to do is define classes that will be published and decorate them with InstrumentationClassAttribute.

[InstrumentationClass(InstrumentationType.Instance)]
public class MyInstance
{
public MyInstance() {}
public string ProcessName;
public string Description;
public string ProcessType;
public string Status;
}

[InstrumentationClass(InstrumentationType.Event)]
public class MyTerminationEvent
{
public MyTerminationEvent() {}
public string ProcessName;
public string TerminationReason;
}

Publishing data is extremely simple:

using System.Management.Instrumentation;
...
MyInstance myInstance = new MyInstance();
MyTerminationEvent myEvent = new MyTerminationEvent();
// Set field values...
Instrumentation.Publish(myInstance);
Instrumentation.Fire(myEvent);

2. Registering WMI Namespace

Now let's take a step back. In order to make above code functional, I need to register WMI namespace for my application. This is done using ManagementInstaller class, but first, I have to decorate the assembly with a special attribute:

[assembly: Instrumented(@"root\MyCompanyName\MyNamespace")]

ManagementInstaller is trivial: it just needs to be added to the Installers collection of my application's Installer class:

[RunInstaller(true)]
public partial class MyAppInstaller : Installer
{
private ManagementInstaller managementInstaller;

public MyAppInstaller()
{
InitializeComponent();

managementInstaller = new ManagementInstaller();
Installers.Add(managementInstaller);
}
}

Now, after I build the application, I can register WMI namespace simply by running "installutil" command against assembly name.

3. Developing WMI Client

Chances are, operations team will ask me to write a WMI client for my provider. No problem, .NET framework has all the tools to get to my application's published data. One approach is to write a query using a SQL-like syntax and execute it using WqlObjectQuery class. Another relies on the ManagementClass object:

ObjectGetOptions options = new ObjectGetOptions(null, new TimeSpan(0, 0, 30), true);
ManagementClass wmiClass = new ManagementClass(WmiScope, "MyInstance", options);
ManagementObjectCollection wmiInstances = wmiClass.GetInstances();

In both cases, I will get back a collection of ManagementObject instances. Although I can extract all the data I want from ManagementObject using field names (e.g., obj["ProcessName"]), I would rather have a strongly typed class to work with. Turns out, there is a .NET tool called "mgmtclassgen" that does exactly that - generates a strongly typed class wrapper for any WMI instance type.

***

WMI is a complex subject, and I realize that I barely scratched the surface in this post. Still, there is enough information to get you started. Good luck!