Want to follow this site? Here's the RSS feed.

ASP.NET Label Contents

Article
Creating JavaScript Components and ASP.NET Controls { ASP.NET, AJAX }
Project
Dynamically Configuring HTTP Handlers in Themelia { ASP.NET, Themelia }
Project
Fundamentals of Themelia - Pipeline Processors (Pre, Mid, Post, and PostState) { ASP.NET, Themelia }
Project
Fundamentals of Themelia - Page Aliasing { ASP.NET, Themelia }
Project
Fundamentals of Themelia - Basic HTTP Routing { ASP.NET, Themelia }
Project
Squid Micro-Blogging Library for .NET 3.5 { WCF, ASP.NET, Projects }
Free Templated Data Bound Custom Controls Chapter { ASP.NET }
ASP.NET 3.5 Web Site and Application { ASP.NET }
Article
10 Things Most Developers Didn't Know in 2007 { Training, Firefox, ASP.NET, AJAX, XHTML/CSS, LLBLGen Pro, Projects }
Article
Creating JavaScript objects from ASP.NET objects { ASP.NET, AJAX }
Article
Real World HttpModule Examples { Training, ASP.NET, LLBLGen Pro }
Article
Simplified Universal HttpHandlerFactory Technique { Training, ASP.NET, LLBLGen Pro }
HnD Customer Support and Forum System { ASP.NET, LLBLGen Pro }
Windows Live Writer RULES { ASP.NET }
Project
NetFXHarmonics SolutionTemplate/E-Book { Training, ASP.NET, AJAX, XHTML/CSS, Projects }
Minima and Data Feed Framework Renamed and Explained { Training, ASP.NET, AJAX, XHTML/CSS, LLBLGen Pro, Projects }
Article
XmlHttp Service Interop - Part 1 (Simple Service Creation) { ASP.NET, AJAX, Services }
Article
The Universal HttpHandlerFactory Technique { ASP.NET }
Video
Data Feed Framework Overview Video { ASP.NET, Videos }
Project
Minima Blog Engine February 2007 CTP Released! { ASP.NET, AJAX, XHTML/CSS, LLBLGen Pro }
User Control Error: The base class includes the field 'xxx', but its type (yyy) is not compatible with the type of control (zzz). { ASP.NET }
Cool SMS/VS2005 Integration Feature { ASP.NET }
Atlas October 2005 { ASP.NET }

Creating JavaScript Components and ASP.NET Controls

Monday, November 10, 2008

Every now and again I'll actually meet someone who realizes that you don't need a JavaScript framework to make full-scale AJAX applications happen... but rarely in the Microsoft community.  Most people think you need Prototype, jQuery, or ASP.NET AJAX framework in order to do anything from networking calls, DOM building, or component creation.  Obviously this isn't true.  In fact, when I designed the Brainbench AJAX exam, I specific designed it to test how effectively you can create your own full-scale JavaScript framework (now how well the AJAX developer did on following my design, I have no idea).

So, today I would like to show you how you can create your own strongly-typed ASP.NET-based JavaScript component without requiring a full framework.  Why would you not have Prototype or jQuery on your web site?  Well, you wouldn't.  Even Microsoft-oriented AJAX experts recognizes that jQuery provides an absolutely incredible boost to their applications.  However, when it comes to my primary landing page, I need that to be extremely tiny.  Thus, I rarely include jQuery or Prototype on that page (remember, Google makes EVERY page a landing page, but I mean the PRIMARY landing page.)

JavaScript Component

First, let's create the JavaScript component.  When dealing with JavaScript, if you can't do it without ASP.NET, don't try it in ASP.NET.  You only use ASP.NET to help package the component and make it strongly-typed.  If the implementation doesn't work, then you have more important things to focus on.

Generally speaking, here's the template I follow for any JavaScript component:

window.MyNamespace = window.MyNamespace || {};
//+
//- MyComponent -//
MyNamespace.MyComponent = (function( ) {
    //- ctor -//
    function ctor(init) {
        if (init) {
            //+ validate and save DOM host
            if (init.host) {
                this._host = init.host;
                //+
                this.DOMElement = $(this._host);
                if(!this.DOMElement) {
                    throw 'Element with id of ' + this._host + ' is required.';
                }
            }
            else {
                throw 'host is required.';
            }
            //+ validate and save parameters
            if (init.myParameter) {
                this._myParameter = init.myParameter;
            }
            else {
                throw 'myParameter is required.';
            }
        }
    }
    ctor.prototype = {
        //- myfunction -//
        myfunction: function(t) {
        }
    };
    //+
    return ctor;
})( );

You may then create the component like the following anywhere in your page:

new MyNamespace.MyComponent({
    host: 'hostName',
    myParameter: 'stuff here'
 });

Now on to see a sample component, but, first, take note of the following shortcuts, which allow us to save a lot of typing:

var DOM = document;
var $ = function(id) { return document.getElementById(id); };

Here's a sample Label component:

window.Controls = window.Controls || {};
//+
//- Controls -//
Controls.Label = (function( ) {
    //- ctor -//
    function ctor(init) {
        if (init) {
            //+ validate and save DOM host
            if (init.host) {
                this._host = init.host;
                //+
                this.DOMElement = $(this._host);
                if(!this.DOMElement) {
                    throw 'Element with id of ' + this._host + ' is required.';
                }
            }
            else {
                throw 'host is required.';
            }
            //+ validate and save parameters
            if (init.initialText) {
                this._initialText = init.initialText;
            }
            else {
                throw 'initialText is required.';
            }
        }
        //+
        this.setText(this._initialText);
    }
    ctor.prototype = {
        //- myfunction -//
        setText: function(text) {
            if(this.DOMElement.firstChild) {
                this.DOMElement.removeChild(this.DOMElement.firstChild);
            }
            this.DOMElement.appendChild(DOM.createTextNode(text));
        }
    };
    //+
    return ctor;
})( );

With the above JavaScript code and "<div id="host"></div>" somewhere in the HTML, we can use the following to create an instance of a label:

window.lblText = new Controls.Label({
    host: 'host',
    initialText: 'Hello World'
});

Now, if we had a button on the screen, we could handle it's click event, and use that to set the text of the button, as follows:

<div>
    <div id="host"></div>
    <input id="btnChangeText" type="button" value="Change Value" />
</div>
<script type="text/javascript" src="Component.js"></script>
<script type="text/javascript">
    //+ in reality you would use the dom ready event, but this is quicker for now
    window.onload = function( ){
        window.lblText = new Controls.Label({
            host: 'host',
            initialText: 'Hello World'
        });
         window.btnChangeText = $('btnChangeText');
         //+ in reality you would use a muli-cast event
         btnChangeText.onclick = function( ) {
            lblText.setText('This is the new text');
         };
    };
</script>

Thus, components are simple to work with.  You can do this with anything from a simple label to a windowing system to a marquee to any full-scale custom solution.

ASP.NET Control

Once the component works, you may then package the HTML and strongly-type it for ASP.NET.  The steps to doing this are very simple and once you do it, you can just repeat the simple steps (some times with a simple copy/paste) to make more components.

First, we need to create a .NET class library and add the System.Web assembly.   Next, add the JavaScript component to the .NET class library.

Next, in order to make the JavaScript file usable my your class library, you need to make sure it's set as an Embedded Resource.  In Visual Studio 2008, you do this by going to the properties window of the JavaScript file and changing the Build Action to Embedded Resource.

Then, you need to bridge the gap between the ASP.NET and JavaScript world by registering the JavaScript file as a web resource.  To do this you register an assembly-level WebResource attribute with the location and content type of your resource.  This is typically done in AssemblyInfo.cs.  The attribute pattern looks like this:

[assembly: System.Web.UI.WebResource("AssemblyName.FolderPath.FileName", "ContentType")]

Thus, if I were registering a JavaScript file named Label.js in the JavaScript.Controls assembly, under the _Resource/Controls folder, I would register my file like this:

[assembly: System.Web.UI.WebResource("JavaScript.Controls._Resource.Label.js", "text/javascript")]

Now, it's time to create a strongly-typed ASP.NET control.  This is done by creating a class which inherits from the System.Web.UI.Control class.  Every control in ASP.NET, from the TextBlock to the GridView, inherits from this base class.

When creating this control, we want to remember that our JavaScript control contains two required parameters: host and initialText.  Thus, we need to add these to our control as properties and validate these on the ASP.NET side of things.

Regardless of your control though, you need to tell ASP.NET what files you would like to send to the client.  This is done with the Page.ClientScript.RegisterClientScriptResource method, which accepts a type and the name of the resource.  Most of the time, the type parameter will just be the type of your control.  The name of the resource must match the web resource name you registered in AssemblyInfo.  This registration is typically done in the OnPreRender method of the control.

The last thing you need to do with the control is the most obvious: do something.  In our case, we need to write the client-side initialization code to the client.

Here's our complete control:

using System;
//+
namespace JavaScript.Controls
{
    public class Label : System.Web.UI.Control
    {
        internal static Type _Type = typeof(Label);

        //+
        //- @HostName -//
        public String HostName { get; set; }

        //- @InitialText -//
        public String InitialText { get; set; }

        //+
        //- @OnPreRender -//
        protected override void OnPreRender(EventArgs e)
        {
            Page.ClientScript.RegisterClientScriptResource(_Type, "JavaScript.Controls._Resource.Label.js");
            //+
            base.OnPreRender(e);
        }

        //- @Render -//
        protected override void Render(System.Web.UI.HtmlTextWriter writer)
        {
            if (String.IsNullOrEmpty(HostName))
            {
                throw new InvalidOperationException("HostName must be set");
            }
            if (String.IsNullOrEmpty(InitialText))
            {
                throw new InvalidOperationException("InitialText must be set");
            }
            writer.Write(@"
<script type=""text/javascript"">
(function( ) {
    var onLoad = function( ) {
        window." + ID + @" = new Controls.Label({
            host: '" + HostName + @"',
            initialText: '" + InitialText + @"'
        });
    };
    if (window.addEventListener) {
        window.addEventListener('load', onLoad, false);
    }
    else if (window.attachEvent) {
        window.attachEvent('onload', onLoad);
    }
})( );
</script>
");
            //+
            base.Render(writer);
        }
    }
}

The code written to the client may looks kind of crazy, but that's because it's written very carefully.  First, notice it's wrapped in a script tag.  This is required.  Next, notice all the code is wrapped in a (function( ) { }) ( ) block.  This is a JavaScript containment technique.  It basically means that anything defined in it exists only for the time of execution.  In this case it means that the onLoad variable exists inside the function and only inside the function, thus will never conflict outside of it.  Next, notice I'm attaching the onLoad logic to the window.load event.  This isn't technically the correct way to do it, but it's the way that requires the least code and is only there for the sake of the example.  Ideally, we would write (or use a prewritten one) some sort of event handler which would allow us to bind handlers to events without having to check if we are using the lameness known as Internet Explorer (it uses window.attachEvent while real web browsers use addEventListener).

Now, having this control, we can then compile our assembly, add a reference to our web site, and register the control with our page or our web site.  Since this is a "Controls" namespace, it has the feel that it will contains multiple controls, thus it's best to register it in web.config for the entire web site to use.  Here's how this is done:

<configuration>
  <system.web>
    <pages>
      <controls>
        <add tagPrefix="c" assembly="JavaScript.Controls" namespace="JavaScript.Controls" />
      </controls>
    </pages>
  </system.web>
</configuration>

Now we are able to use the control in any page on our web site:

<c:Label id="lblText" runat="server" HostName="host" InitialText="Hello World" />

As mentioned previously, this same technique for creating, packaging and strongly-typing JavaScript components can be used for anything.  Having said that, this example that I have just provided borders the raw definition of useless.  No one cares about a stupid host-controlled label.

If you don't want a host-model, but prefer the in-place model, you need to change a few things.  After the changes, you'll have a template for creating any in-place control.

First, remove anything referencing a "host".  This includes client-side validation as well as server-side validation and the Control's HostName property.

Next, put an ID on the script tag.  This ID will be the ClientID suffixed with "ScriptHost" (or whatever you want).  Then, you need to inform the JavaScript control of the ClientID.

Your ASP.NET control should basically look something like this:

using System;
//+
namespace JavaScript.Controls
{
    public class Label : System.Web.UI.Control
    {
        internal static Type _Type = typeof(Label);

        //+
        //- @InitialText -//
        public String InitialText { get; set; }

        //+
        //- @OnPreRender -//
        protected override void OnPreRender(EventArgs e)
        {
            Page.ClientScript.RegisterClientScriptResource(_Type, "JavaScript.Controls._Resource.Label.js");
            //+
            base.OnPreRender(e);
        }

        //- @Render -//
        protected override void Render(System.Web.UI.HtmlTextWriter writer)
        {
            if (String.IsNullOrEmpty(InitialText))
            {
                throw new InvalidOperationException("InitialText must be set");
            }
            writer.Write(@"
<script type=""text/javascript"" id=""" + this.ClientID + @"ScriptHost"">
(function( ) {
    var onLoad = function( ) {
        window." + ID + @" = new Controls.Label({
            id: '" + this.ClientID + @"',
            initialText: '" + InitialText + @"'
        });
    };
    if (window.addEventListener) {
        window.addEventListener('load', onLoad, false);
    }
    else if (window.attachEvent) {
        window.attachEvent('onload', onLoad);
    }
})( );
</script>
");
            //+
            base.Render(writer);
        }
    }
}

Now you just need to make sure the JavaScript control knows that it needs to place itself where it has been declared.  To do this, you just create a new element and insert it into the browser DOM immediately before the current script block.  Since we gave the script block and ID, this is simple.  Here's basically what your JavaScript should look like:

window.Controls = window.Controls || {};
//+
//- Controls -//
Controls.Label = (function( ) {
    //- ctor -//
    function ctor(init) {
        if (init) {
            if (init.id) {
                this._id = init.id;
                //+
                this.DOMElement = DOM.createElement('span');
                this.DOMElement.setAttribute('id', this._id);
            }
            else {
                throw 'id is required.';
            }
            //+ validate and save parameters
            if (init.initialText) {
                this._initialText = init.initialText;
            }
            else {
                throw 'initialText is required.';
            }
        }
        //+
        var scriptHost = $(this._id + 'ScriptHost');
        scriptHost.parentNode.insertBefore(this.DOMElement, scriptHost);
        this.setText(init.initialText);
    }
    ctor.prototype = {
        //- setText -//
        setText: function(text) {
            if(this.DOMElement.firstChild) {
                this.DOMElement.removeChild(this.DOMElement.firstChild);
            }
            this.DOMElement.appendChild(DOM.createTextNode(text));
        }
    };
    //+
    return ctor;
})( );

Notice that the JavaScript control constructor creates a span with the specified ID, grabs a reference to the script host, inserts the element immediately before the script host, then sets the text.

Of course, now that we have made these changes, you can just throw something like the following into your page and to use your in-place JavaScript control without ASP.NET.  It would look something like this:

<script type="text/javascript" id="lblTextScriptHost">
    window.lblText = new Controls.Label({
        id: 'lblText',
        initialText: 'Hello World'
    });
</script>

So, you can create your own JavaScript components without requiring jQuery or Prototype dependencies, but, if you are using jQuery or Prototype (and you should be!; even if you are using ASP.NET AJAX-- that's not a full JavaScript framework), then you can use this same ASP.NET control technique to package all your controls.

(0 Comments)

Dynamically Configuring HTTP Handlers in Themelia

Monday, July 28, 2008

This documentation has been updated for Themelia Framework 2.0 Beta 4.

One of the things that expert ASP.NET developers often find themselves doing is carefully inserting processes at just the right place in the ASP.NET pipeline and page life cycle. It's incredibly important for developers to have a strong familiarity with the ASP.NET order of operations. If you do something too early, a control may not exist or session may not be initialized. If you do it too late, any data you set will be overwritten.

Fortunately, if you aren't familiar with these concepts, there are many resources available to us. For example, Rick Strahl has written "A low-level Look at the ASP.NET Architecture" to help developers understand the order of these operations. Furthermore, MSDN contains an overview of the page life cycle eggheadcafe.com has a very detailed reference image.  Then there's a PowerPoint presentation by the queen of all architecture everywhere, Michele Leroux Bustamante, entitled "Inside the ASP.NET Runtime - Intercepting HTTP Requests".  For those of us who are already well versed in the ASP.NET pipeline and page life cycle, these are great reference to which we can turn when we (by we I mean me) forget a thing or two after not doing any pipeline hacking for a while.

Themelia Order of Operations

Within the ASP.NET pipeline and the page life cycle, Themelia runs after all HTTP modules have run, but, because Themelia is the creator of the HTTP handler, before an HTTP handler has been created. This means that you are able to modify HTTP handler properties or even call HTTP handler method before the handler themselves ever run. Granted, the former situation makes more sense than the latter, but the option for method call does exist.

To understand how this is possible, you must first understand the various parts of Themelia as explained in previous posts.  You must also know the Themelia order of operations. Here is a quick run down of the order of operations within Themelia:

  • ConfigurationPreProcessor (loads configuration information)
  • DebugPreProcessor runs
  • WebDomainPreProcessor runs
  • DefaultPagePreProcessor runs
  • Web Components Load
  • PreProcessors run
  • Exit if PassThroughHttpHandler.ForceUse was set
  • Handler Factory Load
  • Injection Processor Load
  • MidProcessors run
  • If no handler was set yet, HTTP Handler Matching Happens; otherwise skip to post processing
  • If no handler is found, the fall through processor runs
  • If no handler is found, pass the request through to IIS
  • PostProcessors run
  • SessionPostPostProcessor runs
  • PostStateProcessors run
  • Continue ASP.NET Pipeline

Dynamically Configuring HTTP Handlers

As this information demonstrates, an HTTP handler will always be set by the time the post processors run. Therefore, if you create an HTTP handler containing properties, you may use a postprocessor to configure that handler.  By doing this, when the HTTP handler itself runs, it will have the information required for its own logic.

For instance, if you want to create an efficient Windows Workflow Foundation ("WF") sales reporting workflow activator, you could create a lightweight HTTP handler that did nothing but run that specific workflow. Using Themelia, you may then create a postprocessor designed to configure that specific HTTP handler. This configuration might be nothing more than setting a the workflow activity type.  The following snippet shows what we're talking about:

public class SalesStatisticsWorkflowPostProcessor : Themelia.Web.Routing.PostProcessorBase
{
    public override IHttpHandler OnPostProcessorExecute(HttpContext context, IHttpHandler activeHttpHandler, params Object[] parameterArray)
    {
        ReportingWorkflowHttpHandler handler = activeHttpHandler as ReportingWorkflowHttpHandler;
        if (handler != null)
        {
            handler.ReportWorkFlowActivity = "Sales.Workflow.StatisticsActivity, Sales.Workflow";
        }
        //+
        return handler;
    }
}

In this particular example, the HTTP handler is abstracted away from any workflow activity.  It's the post processor that provides this information.  Developers could extend this further by utilizing Themelia web components, creating prepacked processors per department for specific workflow activity sets. Or, if the developers really thought ahead, they could use some of Themelia's supporting helping features to determine what workflow HTTP handler to run based upon a REST-style URL. For example, take a look at the following Themelia postprocessor with its associated registration:

public class SalesWorkflowPostProcessor : Themelia.Web.Routing.PostProcessorBase
{
    public override IHttpHandler OnPostProcessorExecute(HttpContext context, IHttpHandler activeHttpHandler, params Object[] parameterArray)
    {
        ReportingWorkflowHttpHandler handler = activeHttpHandler as ReportingWorkflowHttpHandler;
        if (handler != null)
        {
            String pattern = "Sales.Workflow.{0}Activity, Sales.Workflow";
            if (Http.GetHttpPart(Http.Position.Antepenultima) == "report")
            {
                String reportName = Themelia.Case.GetPascalCase(Http.GetHttpPart(Http.Position.Ultima));
                handler.ReportWorkFlowActivity = String.Format(pattern, reportName);
                handler.Region = Http.GetHttpPart(Http.Position.Penultima);
            }
        }
        //+
        return handler;
    }
}

Here is the associated HTTP handler registration for ReportWorkflowHttpHandler (obviously, there would be a aliased handler factory used here, but that's not important for out discussion):

<add matchType="contains" name="ReportingWorkFlow" text="/report/" />

In this example, the postprocessor checks to see if the HTTP handler that is going to run is ReportingWorkFlowHttpHandler.  If so, the postprocessor checks to see if the third to the last part of the URL is "report". If this is the case, then the last part of the URL is set as the workflow activity name and the second to the last part of the URL is set as the workflow sales region. For example, "http://www.netfxharmonics.com/workflow/report/signapore/demographic/" would run the "Sales.Workflow.DemographicActivity, Sales.Workflow" while "http://www.netfxharmonics.com/workflow/report/kansascity/annual/" would run the "Sales.Workflow.AnnualActivity, Sales.Workflow". The custom workflow HTTP handler would also have the region information ready to send to WF.

There's one final concluding thought that I would like to mention in passing. Since Themelia provides the SessionPostStateProcessor which allow adding to session before session state is initialized, adding to ASP.NET session is essentially available at every point in Themelia. Therefore, you may feel free to utilize session in your processors as you see fit. In this example, perhaps you would like to create a preprocessor that pulls the penultimate (second to the last) position of the URL and inserts a custom object into session based on that information so all future processors will have it available.

Links

Fundamentals of Themelia - Pipeline Processors (Pre, Mid, Post, and PostState)

Wednesday, June 18, 2008

This documentation has been updated for Themelia Framework 2.0 Beta 5.

Themelia has various processors which handle allow for custom logic at various points in the Themelia pipeline.  These processors include pre processors, mid processors, post processors, and post state processors and since they work directly on the pipeline they are referred to as the pipeline processors. I'll just run through them, explaining what they are, how to build and use them and also give a few examples.

Pre Processors

PreProcessors allow the developer to do any custom logic that is required to run after HTTP modules, but before any other part of the system. These also run before any HTTP handlers as well as before any other feature of Themelia. You can use preprocessors for many things, but they were originally designed to setup the environment for the HTTP handlers.

For instance, perhaps you want to parse the query string, set a context item, detect for a certain browser or even want to pull something directly from the HTTP POST or modify viewstate. This is a good place to do those things. All your settings will be in place before your HTTP handlers are called and, therefore, before web forms even think about loading. So, this is your chance, if you are using webforms, to do processing to control data even before Init, PreInit, and even the web form constructor!

One example of a preprocessor is in Minima called MinimaPreProcessor. Minima uses this feature to read the BlogGuid from configuration so that the other HTTP handlers will have it available. Themelia itself also uses preprocessors, but these are used to support features that will be discussed in a future post. For now, however, feel free to look at the Themelia source code to see these examples of preprocessors.

Another example of a PreProcessor is in Themelia itself.  Something has to read the Themelia configuration, right?  Internally, Themelia uses the ConfigurationPreProcessor to do all configuration processing so that the rest of the system will have all the information it needs.  There are

A preprocessor is made by creating a class that inherits from Themelia.Web.Routing.PreProcessorBase. This is an abstract class that requires you to implement the following signature. However, the params array in this signature is used for Themelia internal use, for Themelia web components as well as for processor initialization, the latter being topics for future discussions.

void OnPreProcessorExecute(System.Web.HttpContext context, params Object[] parameterArray)

Though this isn't the place for a full discussion of it, pre-processing may be the last thing that runs in certain cases.  This possibility comes from a Themelia feature called PassThrough.  For now, be aware, that there's more going on than the execution of the various processors written about here.

Mid Processors

Mid processors are a bit similar to preprocessors in that their sole purpose is to do any form of processing that you need. However, these are done after preprocessors have run, handler factories have loaded, and after injection processors (discussed elsewhere) have injected their handlers, but just prior to HTTP handler selection. This gives you an opportunity to use the information already provided to you by the other Themelia features to possibly alter the routing process in some way.

Probably the most obvious use of a mid processor is in detecting to see if the information already provided gives enough information to skip the entire HTTP handler selection process, which would otherwise happen just after all mid processors run. For example, based on certain security information provided to you in a custom preprocessor, you may already know you need to use a specific injected HTTP handler.

A mid processor is made by creating a class that inherits from Themelia.Web.Routing.MidProcessorBase. This is an abstract class that requires you to implement the following signature:

IHttpHandler OnMidProcessorExecute(HttpContext context, params Object[] parameterArray)

By returning an HTTP handler, the HTTP selection process is skipped.  By returning null, routing continues as usual.

Fall Through Processors (not a Pipeline Processor)

Though, there's more to it than this, after mid processors have executed, Themelia runs a HTTP handler selection algorithm to find the best match for the specific request.  If there is no handler match, a fall through processor, if set, catches the route and handles it itself. If no fall through processor, which isn't actually a pipeline processor) is provided or if all registered fall through processors return null, Themelia defaults to passing the request back to IIS to let if figure out the route.

However, due to the fact that they are so different than these other types of processors, they are discusses in a separate place.  For the time being though, just know that they run after mid-processors, but before post-processors.  After you see a few examples of this and read some more docs, this should all make more sense.

Post Processors

Post processors are a bit similar to pre processors and mid processors. However, these are the very last processors to run in the route activator.  This means that run after fall through processors (discussed elsewhere), giving you one last chance to override something a previous processor or even the fall through processor may have done.  In fact, this is where you could set a handler if the fall through processor didn't.  If you want to switch to a different HTTP handler based on some custom logic, this is your time to do it.

A post processor is made by creating a class that inherits from Themelia.Web.Routing.PostProcessorBase. This is an abstract class that requires you to implement the following signature:

IHttpHandler OnPostProcessorExecute(HttpContext context, IHttpHandler activeHttpHandler, params Object[] parameterArray)

As you can see from this signature, the currently active HTTP handler comes in as a parameter.  This means you may use the HTTP handler at this point for some reason.  Perhaps you would like to set a few properties on the handler or even call a handler method.

By returning an HTTP handler, you are effectively override all previous processors. By returning null, the existing HTTP handler remains and routing continues as usual.

One final note that shouldn't be overlooked is that at any point in the Themelia pipeline, you can send a signal to skip post-processing.  This should obviously be used with extreme caution, but if you do want to skips this processors, you may call the FlowControl.SkipPostProcessing() method and post processing won't execute.

PostStateProcessors

Post state processors are similar to preprocessors in that they exist simply to work with information.  A preprocessor may load some information to prepare the pipeline whereas a post state processor may do some last-chance processing on information from the pipeline.  A post state processor, as its name suggests, runs after ASP.NET state has been initialized and therefore has full access to session information.  So, if you need to configure some information in session, this is your place to do it.  However, remember that by the time a post state processor runs, your HTTP handler has already been set and you are not allowed to change it.

This type of processor is actually the only processor that has direct access to session information.  However, that doesn't mean that you can't set session in other processors.  How's that possible if you don't have direct access to session?  Themelia internally has a post state processor called SessionPostStateProcessor which runs before any custom post state processors.  This post state processor has a single object in it called Data of StringObjectMap.  Any information set in this single, will be copied to session before custom post state processors runs.

As an example, the following preprocessor, while it doesn't have access to session, registers a string to session via the SessionPostStateProcessor:

public class AuthenticationPreProcessor : PreProcessorBase
{
    public override void OnPreHttpHandlerExecute(HttpContext context, params Object[] parameterArray)
    {
        SessionPostStateProcessor.Data["UserName"] = "John Doe";
    }
}

When using this or the even items context, session data, or cache data, it's generally a good idea to scope your information using the scope operator.  The above example is rather poorly written because the session item name "UserName" is rather general and may be overwritten by someone else.  Therefore, something like the following is recommend:

public class AuthenticationPreProcessor : PreProcessorBase
{
    //- @Info -//
    public class Info
    {
        public const string Scope = "Authentication";
    }

    //- @OnPreHttpHandlerExecute -//
    public override void OnPreHttpHandlerExecute(HttpContext context, params Object[] parameterArray)
    {
        SessionPostStateProcessor.Data[Info.Scope + "::UserName"] = "John Doe";
    }
}

Themelia inherently understands item scope and uses it at many places internally.  To access the above information after it's in session, you may use the following:

Themelia.Web.HttpData.GetScopedSessionItem<String>(AuthenticationPreProcessor.Info.Scope, "UserName");

A post state processor is made by creating a class that inherits from Themelia.Web.Routing.PostStateProcessorBase. This is an abstract class that requires you to implement the following signature:

void OnPostStateProcessorExecute(HttpContext context, params Object[] parameterArray)

This signature should look familiar as it's basically the same signature as a preprocessors.

These four types of processors give you a wide variety of control over the flow of your routing.  Each of these concepts give you a measure of control over the entire HTTP pipeline.  However, these are intended to be understood by themselves.  Without, for example, a full understanding of fall through processors, the flow of the Themelia pipeline is incomplete.

Registration

Registering any type of processor in Themelia is as simple as providing the site's web.config file with the processor type:

<themelia.web>
  <webDomains>
    <add>
      <processors>
        <add type="Sample.Web.Routing.PreProcessor, Sample.Web" />
        <add type="Sample.Web.Routing.MidProcessor, Sample.Web" />
        <add type="Sample.Web.Routing.PostProcessor, Sample.Web" />
        <add type="Sample.Web.Routing.PostStateProcessor, Sample.Web" />
      </processors>
    </add>
  </webDomains>
</themelia.web>

Processor Factories

No one likes to type long fully qualified types.  Therefore, Themelia provides various categories of factories to help register various pieces.  For factories, we have processor factories.  By using a processor factory we can turn the above configuration into the following:

<themelia.web>
  <webDomains>
    <add>
      <processors>
        <add type="PreProcessor" />
        <add type="MidProcessor" />
        <add type="PostProcessor" />
        <add type="PostStateProcessor" />
      </processors>
    </add>
  </webDomains>
</themelia.web>

For more information on processor factories, see the processor factory documentation.

Links

Fundamentals of Themelia - Page Aliasing

Thursday, June 12, 2008

This documentation has been updated for Themelia Framework 2.0 Beta 5.

One of the core HTTP handlers that ship with Themelia is the page alias handler, which allows users to access SEO-friendly (search engine optimization) paths. For example, a user would see /login/ instead of something disgusting like /Login.aspx.  Themelia page aliasing is similar to a concept called URL rewriting mechanism (though with major differences discussed further down).

Registering a Themelia page alias is done in two steps: first, register the path to a PageAlias handler and, second, register the alias with the alias configuration collection in Themelia, linking them together with a key.  The key is the key attribute on the alias and the text attribute or, if present, the referenceKey attribute on the handler declaration.  The key is very important.  While, as is explained in basic HTTP routing, handlers are loaded based on rules of specificity, aliases are loaded via the key.

Below is an example configuration snippet; notice that the HTTP handler name has an alias of "PageAlias", meaning it's registered in Themelia's internal HTTP handler factory.

<themelia.web>
  <webDomains>
    <add>
      <handlers>
        <add matchType="contains" name="PageAlias" text="/login/" />
        <add matchType="contains" name="PageAlias" text="/logout/" />
        <add matchType="contains" name="PageAlias" text="/contact/" referenceKey="c" />
      </handlers>
      <aliases>
        <!--login-->
        <add key="/login/" target="/Sequence_/Security.aspx" />
        <add key="/logout/" target="/Sequence_/Security.aspx" />
        <!--suggestion-->
        <add key="c" target="/Sequence_/Contact.aspx" />
      </aliases>
    </add>
  </webDomains>
</themelia.web>

Here you can see that the login and logout aliases are keyed to their respective handlers via the text while the contact alias is keyed via the referenceKey.  The referenceKey attribute is completely optional.  You may use it all of the time, some of the time, or none of the time.  However, as a common sense practice, you want to be congruent (not "consistent"; consistency says "I always drive 45mph.  Doesn't matter if it's a school zone or the interstate" while congruency says "I will drive according to the common sense underlying principles of my current situation: 18mph is a school zone and 65mph on the interstate.)  Therefore, you may want to either always use the referenceKey, or only use it when the handler text gets beyond 20 characters.

Now, by way of commentary, in the above example, when a user accesses either /login/ or /logout/, he or she will actually be sent /Page_/Security/Login.aspx under the covers. But what would differentiate between the two in /Security/Login.aspx? The answer is obvious: just check to see which the user is accessing. In the following example, you can also see how Themelia provides you with simple helper mechanisms:

public partial class Login : Sample.ThemeliaPage
{
    //- @Setting -//
    public class Setting
    {
        public const string IsAuthenticated = "IsAuthenticated";
    }

    //+
    //- #OnInit -//
    protected override void OnInit(EventArgs e)
    {
        if (Themelia.Web.Http.UrlPartArray.Contains("logout"))
        {
            Themelia.Web.HttpData.SetSessionItem<Boolean>(Setting.IsAuthenticated, false);
        }
        //+ check security
        if (Themelia.Web.HttpData.GetSessionItem<Boolean>(Setting.IsAuthenticated))
        {
            Themelia.Web.Http.Redirect("/");
        }
        //+
        base.OnInit(e);
    }
}

Themelia provides you with a class called Themelia.Web.Http which contains various members to aide development, including a UrlPartArray member which is an array containing the different parts of the virtual path accessed. In this case UrlPartArray[0] would have "Login" or "Logout". C# 3.0 LINQ is being used to check for this. Themelia.Web.HttpData also includes a number of generic methods to help you access namespaced (scoped) session, cache, and context items in a strongly typed manner.

Regular Expression Match and Variable Capture

One important thing you can do with page aliasing is use a regular expression to create patterns of matching.  You use a typical regular expression matching pattern with a $x replacement.

You need to be careful using this, however, as over use will create sloppiness.  You don't want to map all of one logical path to all of a physical path as this brings you right back to the tight coupling between the logical and physical structure.  For example, it's a very bad idea to use /product/([a-z]+)/$ to map to "/Page_/Product/$1.aspx.  That does absolutely nothing more than reconnect your logical and physical structures.  The point is to separate them, not to create a pattern that connects them just as tight as when you started.

Therefore, in order to create a clean, loosely-coupled structure, regular expressions should be used primarily for variable capture.  Here's an example:

<themelia.web>
  <webDomains>
    <add defaultPage="/Page_/Security/Home.aspx">
      <handlers>
        <add matchType="contains" name="PageAlias" text="/problem/([\-a-z0-9]+)" referenceKey="problem" />
      </handlers>
      <aliases>
        <add key="problem" target="/Page_/Quality/Problem.aspx?guid=$1" />
      </aliases>
    </add>
  </webDomains>
</themelia.web>

Notice a few thing about this. First, the regular expression in the handler text copies a variable to the $ variable in the alias.  If there were more than one capture, the number in the alias increments (i.e. $1, $2, $3, etc...)  Second, while you may still use the handler text as the key, regular expressions can be long and complex, therefore, it's probably a good idea to use the referenceKey for this.

One critical point about Themelias aliasing is that it's not technically "rewriting".  Rewriting is an old concept which actually changes the user's request.  So if /problem/([a-z]+) is rewritten to /Problem.aspx?guid=$1, then when /problem/asdf is accessed, HttpContext.Current.Request.Url.AbsoluteUri will show /Problem.aspx?guid=asdf.  Not very helpful.  Therefore, Themelia aliasing thus not rewriting anything, but just mapping a logical path to a physical one, allow the absolute URI to remain as /problem/asdf.

Now since we are talking about variable capture, the data must be captured somewhere.  Given the fact that the above looks like a query string you would think that you would have to access the data as a query string.  However, if that were the case, then you wouldn't actually be able to use a query string (i.e. /problem/asdf?hello=qwer).  Therefore, Themelia provides capture state accessible via Themelia.Web.HttpData.

You may access a single captured item via Themelia.Web.HttpData.GetCaptureItem or you may obtain all captured items via Themelia.Web.HttpData.GetCaptureItemMap( ).

Here's an example of getting the data The following example demonstrates this:

//- $OnSubmit -//
private void OnSubmit(Object sender, EventArgs e)
{
    if (Http.GetUrlPart(Http.Position.Penultima) == "problem")
    {
        //+ in this case, you could get this from Http.GetUrlPart(Http.Position.Ultima) in
        String guid = HttpData.GetCaptureItem("guid");
        Sample.Web.ExceptionSaver.Save(guid, txtDescription.Text);
    }
}

In a world of rewriting, your ability to use query string is just about gone.  Since /problem/adsf IS /Problem.aspx?guid=asdf, /problem/adsf?hello=qwer is very confusing to understand.  Therefore, in Themelia, you may still use a query string.  You may use Themelia.Web.HttpData.GetQueryItem to access a query item in the same place where you use GetCaptureItem(String item) to access a captured item.  Also, as you may have expected, you may use Themelia.Web.HttpData.GetQueryMap to access all query items.

For more information on Themelia HTTP data see the HttpData class documentation.

Initialization Parameters

You may also use Themelia initialization parameters with aliasing.  This essentially means that you can give handlers parameters that they may use for inline initialization.  The handlers have to be aware of the parameters (which is a more advanced topic), but the page alias handler is already initialization parameter aware.  Below is an example of using initialization parameters with the page alias handler.

<handlers>
  <add matchType="contains" name="PageAlias" text="/problem/([\-a-z0-9]+)">
    <parameters>
      <add name="target" value="/Page_/Quality/Problem.aspx?guid=$1" />
    </parameters>
  </add>
</handlers>

The parameters here are the exact same parameters you would set in the <aliases> section.  If you are doing a series of aliases, perhaps you would rather keep them in the <aliases> section, however, if you are only doing one perhaps initialization parameters are more up your alley.

Page Alias Fall Through Processor

(This section requires knowledge of Themelia fall through processors.)

In some situations, perhaps you have an entire web domain or perhaps an entire web site that is full of page aliases and you don't feel like declaring each one of them as handlers.  In this situation, you can use the PageAliasFallThroughProcessor.  The following configuration example demonstrates how to enable this type of scenario:

<themelia.web>
  <webDomains>
    <add>
      <handlers>
        <add matchType="pathStartsWith" name="Authentication" text="/authenticate/" />
      </handlers>
      <aliases>
        <!--login-->
        <add key="/login/" target="/Sequence_/Security.aspx" />
        <add key="/logout/" target="/Sequence_/Security.aspx" />
        <!--suggestion-->
        <add key="/contact/" target="/Sequence_/Contact.aspx" />
      </aliases>
      <processors>
        <add type="PageAliasFallThroughProcessor" />
      </processors>
    </add>
  </webDomains>
</themelia.web>

Remember, fall through processors only run if there if there isn't an explicit route for your current path.  Therefore, the /authentication/ path will still go to the Authentication HTTP handler, while everything else will go to the PageAliasFallThroughProcessor.

With these simple page aliasing features, you can kill off all those nasty ".aspx" files off all your pages. That one simple fix will make your entire web site look much more professional and make it much easier for users to remember your links. It also provides a nicely structured web site without a splurge of superfluous physical folders and default documents (i.e. default.aspx) laying all over the place, which, in turn, gives you a more centralized file management experience.

Links

Fundamentals of Themelia - Basic HTTP Routing

Wednesday, June 4, 2008

This documentation has been updated for Themelia Framework 2.0 Beta 5.

Themelia (Th-meh-LEE-uh; Greek for foundations) is the foundation for every web site and web application that I have built in the past few years.  While there are many aspects to Themelia, just about everything in Themelia exists to support its HTTP routing system which provides a completely new paradigm for all ASP.NET development.

In this entry, I'm going to give a basic introduction to Themelia 2.0 HTTP routing.  Be aware, however, that the work basic doesn't even do this justice.  This is the tip of the ice berg of basic.

Simple HTTP Handler Registration

In any of my web applications, I'll have anywhere from 5 to 30 HTTP handlers. They are small, powerful, and easy to build. When I don't need a web form, I don't need the its associated overhead either. If you need a simple authentication endpoint, a place for your Facebook or PayPal application to call back, or just need a simple non-WCF HTTP endpoint to receive and process data, then you seriously need to look into using an HTTP handler. Below is a sample HTTP handler.

namespace Sample
{
    public class AuthenticationHttpHandler : System.Web.IHttpHandler
    {
        //- @IsReusable -//
        public Boolean IsReusable
        {
            get { return true; }
        }
        //- @ProcessRequest -//
        public void ProcessRequest(System.Web.HttpContext context)
        {
            context.Response.Write("Hello");
        }
    }
}

However, one of the problems with HTTP handlers is that they are simple classes that implement the System.Web.IHttpHandler interface and, therefore, have no immediate access endpoint. ASP.NET does, however, provide a file extension called ASHX which it registers with IIS to provide HTTP handlers an endpoint. Personally, I've always found that to be an extremely sloppy technique as it requires you to have an ASHX file for every HTTP handler. That's not going to happen. Furthermore, uhh... file extensions suck!

A much more effective mechanism for creating and HTTP handler endpoint is to register it in your web.config file with a specific path (i.e. /MyHandler.aspx or /MyHandler/). You get the same result without having to create a completely superfluous file. However, to make the latter endpoint accessible in IIS6, you need to setup a wildcard mapping to ASP.NET. An alternative and more powerful way of doing this is by using a very carefully designed HTTP module.

The major downside here is that HTTP handler registration is different between NetFXHarmonics DevServer/IIS6 and II7. You have to register each handler once in the <system.web> section for DevServer and IIS6 and once again in the <system.webServer> section for IIS7.  Furthermore, if you are using the IIS7 integrated pipeline, you have to remove the IIS6 registrations or an exception will be thrown.  While I find this duplicate registration to be 100x better than actually creating a ton of ASHX files that do little more than cluttering the world, it's still too much work.

Therefore, Themelia allows you to have one central registration point for all HTTP handlers. It does this by providing a HTTP module which handles all web site traffic.  Whereas HTTP handlers handle a specific HTTP request,  HTTP modules handle everything everywhere.  To use Themelia 2.0, you just have to register it's RoutingModule as an HTTP module in IIS6 or IIS7. Now, instead of registering 30+ handlers twice, you are registering your specific 30+ handlers in Themelia's centralized location. In the following example you can see the basic mechanism for registering Themelia with your ASP.NET application.

<system.web>
  <httpModules>
    <add name="Routing" type="Themelia.Web.Routing.RoutingModule, Themelia.Web" />
  </httpModules>
</system.web>
<system.webServer>
  <modules>
    <add name="Routing" preCondition="" type="Themelia.Web.Routing.RoutingModule, Themelia.Web" />
  </modules>
</system.webServer>

With this simple module registration, you application is running on Themelia 2.0.  However, it's not really doing much yet.  Configuration is done in web.config and may go from extremely simple to rather complex (not to be confused with "complicated"; complex just means "multiple parts").  In further documentation, the configuration will become more and more complex providing for more power and flexibility.

However, as with any custom .NET configuration, you must first register the configuration section at the top of your web.config file as the following snippet shows (or to register for all ASP.NET application on your server use your machine.config instead -- %systemroot%\Microsoft.NET\Framework\v2.0.50727\CONFIG\machine.config):

<configuration>
  <configSections>
    <section name="themelia.web" type="Themelia.Web.Configuration.ThemeliaWebConfigurationSection, Themelia.Web"/>
  </configSections>
</configuration>

In terms of actually configuring Themelia, one of the easiest configurations is seen in the below example taken from Themelia's "Basic" sample.  This sample does a simple registration of the "Authentication" HTTP handler.

<themelia.web>
  <webDomains>
    <add>
      <handlers>
        <add matchType="contains" name="Authentication" text="/authenticate/" />
      </handlers>
    </add>
  </webDomains>
</themelia.web>

The matchType may have one of eight possible types: "startsWith, "endsWith", "contains", "pathStartsWith", "pathContains", "pathEquals", "webDomainPathEquals", and "webDomainPathStartsWith".  The first three match against the entire URL while the next two only match against the path of the URL after the prototcol, address, and port.  The last one requires knowledge of web domains and is discussed in the web domains documentation.  As an example, the first three will match against "http://127.0.0.1/data/processing.svc" while "pathStartsWith" and "pathContains" will only match against  "/data/processing.svc".

Handler Selection

When a request comes in, Themelia uses the information provided it to select the proper action to take.  When dealing with handler registration, there is a handler selection process which chooses the correct handler.  However, more than one handler may satisfy a particular request.  For example, the URL path of /user/download/ would match both a matchType of "contains" and "endsWith".  If the two handler registrations send the request to two different places, there's a big result difference.  Even then, if two handler registrations are matching with "contains", but one is matching against /user/ and the other is matching against /user/download/, we again have a situation to deal with.

Fortunately, the solution to the problem is obvious.  Themelia internally internally sorts all handlers by matchType in logical order of specificity (the measure of how specific something is) and then by text length.  Here's the specificity order:

  • pathEquals
  • endsWith
  • webDomainPathStartsWith
  • webDomainPathEquals
  • pathStartsWith
  • startsWith
  • pathContains
  • contains

Regular Expression Shorthand

In addition to the above matching keywords, you may also use a form of regular expression shorthand.  For example, you can begin your handler text with "^" and it will automatically change your match type to "startsWith".  In the same way, ending your handler text with "$" will automatically change it to "endsWith".  For example, "/authenticate/$" converts the matchType to "endsWith".  Here's a complete list:

  • Beginning with "wdp^" and ending with "$" means "webDomainPathEquals"
  • Beginning with "p^" and ending with "$" means "pathEquals"
  • Beginning with "^" and ending with "$" means "equals"
  • Beginning with "wdp^" by itself means "webDomainPathStartsWith"
  • Beginning with "p^" by itself means "pathStartsWith"
  • Beginning with "^" by itself means means "startsWith"
  • Ending with "$" by itself means means "endsWith"

These override anything that you put in the matchType attribute.

HTTP Handler Aliasing

Here's a simple quiz to see if you are paying attention or to see how well you know ASP.NET: What HTTP handler will Themelia match for /authenticate/? Go ahead, move your eyes up a few paragraphs and check it out.  The answer is simple: you have no idea because "Authentication" is the name of the handler, not a valid .NET type. In this example, Themelia is utilizing an, optional, feature called an handler factory to obtain the HTTP handler.

A HTTP handler factory is a class that inherits from the Themelia.Web.Routing.HandlerFactoryBase class and implements the following method signature, which accepts a handler alias as a parameter and returns an HTTP handler instance:

IHttpHandler CreateHttpHandler(String text)

Using a handler factory, you can create aliases to allow yourself and others easier access to HTTP handlers. In this case "Sample.AuthenticationHttpHandler, Sample" was shortened to the alias "Authentication" with the following Sample.SampleHandlerFactory class.

namespace Sample
{
    public class SampleHandlerFactory : Themelia.Web.Routing.HandlerFactoryBase
    {
        //- @CreateHttpHandler -//
        public override IHttpHandler CreateHttpHandler(String text)
        {
            switch (text)
            {
                case "auth":
                case "authentication":
                    return new AuthenticationHttpHandler();
            }
            //+
            return null;
        }
    }
}

As you can see, because C# allows contentless fall-through in switch statements, you can very easily create more than one alias for an HTTP handler. Therefore, the following handler registration would work too:

<themelia.web>
  <webDomains>
    <add>
      <handlers>
        <add matchType="contains" name="Auth" text="/authenticate/" />
      </handlers>
    </add>
  </webDomains>
</themelia.web>

You may have any number of handler factories, perhaps one for each logical grouping of handlers or to make the handler names more streamlined. For example, if you are creating a series of callback endpoints for various social networking services, maybe you want to create a HTTP handler factory which shortens the names of the handlers involved and suffixes them with "Callback" like "FacebookCallback" and "PayPalCallback".

Handler factories, like all Themelia factories, are registered in the web.config under the <factories> collection. The following configuration snippet demonstrates this:

<themelia.web>
  <webDomains>
    <add>
      <factories>
        <add type="Sample.Web.HandlerFactory, Sample.Web" />
      </factories>
    </add>
  </webDomains>
</themelia.web>

It's important to realize that http handler factories are completely optional. They just make future development easier to think about. If you have HTTP handlers you haven't yet put into a http handler factory, you can still register the handler in the name attribute using a fully qualified .NET name like the following example.  However, be aware that if you do not use a handler factory, obviously Themelia will be dynamically creating that type on the fly. So, it's strongly encourages that you always use an handler factory even if you have only one handler.

<themelia.web>
  <webDomains>
    <add>
      <handlers>
        <add matchType="contains" name="Sample.Web.AuthenticateHandler, Sample.Web" text="/authenticate/" />
      </handlers>
    </add>
  </webDomains>
</themelia.web>

Temporarily Disabling Themelia

You may also disable all Themelia routing by setting the disableRouting configuration attribute to true as the following example shows:

<themelia.web disableRouting="true">
</themelia.web>

Web Domains

There's another concept in Themelia 2.0, namely a WebDomain, but it's a topic beyond the scope of this discusses.  However, it's important to know that a web domain is essentially a web AppDomain.  Whereas an AppDomain separates segments of an application, a web domain separates the segments of a web site.  For a single-web domain Themelia application, only the "root" (or default) web domain is used.  That's why you see the <webDomains><add></add></webDomain> stuff in the examples.

Links

Squid Micro-Blogging Library for .NET 3.5

Monday, March 17, 2008

A few years ago I designed a system that would greatly ease data syndication, data aggregation, and reporting.  The first two components of the system were repackaged and release early last year under the incredibly horrible name "Data Feed Framework".  The idea behind the system was two fold.  The first concept was that you write a SQL statement and you immediately get a fully functional RSS feed with absolutely no more work required.  Here's an example of a DFF SQL statement that creates an RSS feed of SQL Server jobs:

select Id=0,
Title=name,
Description=description
from msdb.dbo.sysjobs
where enabled = 1

The second part of DFF was it's ASP.NET control named InfoBlock that would accept an RSS or ATOM feed and display it in a mini-reader window.  The two parts of DFF combine to create the following:

Given the following SQL statement (or more likely a stored procedure)...

select top 10
Id=pc.ContactID, 
Title=pc.FirstName + ' ' + pc.LastName + ': $' + convert(varchar