February 27, 2008


ResolveExternalLinksComponent is a Sandcastle build component that I wrote to solve a general usability problem that was reported here, by Karl, and to dog food my Hosted Sandcastle Build Component Template.  The component's current behavior was designed with Karl's help.  Thanks Karl :)

Preliminary documentation for the component is here.

Note that the latest release of the component is built-in to DocProject 1.10.1 - a separate download is not required.  When you create a new DocProject or DocSite the component will be included automatically in the Help1.x and Help 2.x reference stacks so that you can begin to use its features right away in your XML documentation comments.


The original problem, to paraphrase, was that Karl had to keep writing repetitive URLs for <see href="url"/> links throughout his XML documentation comments.  The common URLs he was using pointed to third-party online documentation and each URL shared the same base, so only the relative portion changed between links.  To simplify the process he wanted to write only the relative path each time a link was required, specify the base URL only once, somewhere else, and then have some magic combine them into an absolute path, post-compilation.

As it turns out, a Sandcastle build component was right for the job.

I liked the idea of using IDs instead of hard-coding common URLs throughout the documentation and felt that this would be a feature that I might use, so I developed the component to resolve <see xref="id"/> into hyperlinks using pre-configured ID-to-URL mappings.  For Karl's particular scenario I added support for the vref attribute, which adds additional semantics to the ID so that it will act as the URL itself if a mapping is not defined.

During development I noticed a few opportunities to add more features, which I did, like being able to associate a group of ID-to-URL mappings with any combination of optional attributes.   In particular, a base URL, hyperlink target, auto-generated reference topic ID or import file.  The component allows for any number of mapping groups with any combination of these attributes, allowing for some pretty interesting scenarios.

For example, in one scenario you might define common URLs as IDs for all of your documentation but then override the URLs in one particular topic to point elsewhere, without even having to modify the XML comments in the API.

Another possibility would be to group mappings by hyperlink target.  So, for example, you could set the default target for all mapped links to open in the same browser frame when clicked but then configure a group of links that reference a third-party website to open in a new browser window when clicked.

The component has a graphical editor that works in DocProject and the Sandcastle Help File Builder, which makes configuring it really simple.  It also provides a few DocProject-specific features, such as providing a list of topic IDs that you can choose from, defaulting to the project's settings directory when using an external mappings file, using a relative path to an external mappings file, automatically including an external mappings file as a project item and expansion options for build component stacks in the DocProject Properties window.

What's New In Beta 2

The next release has a few bug fixes and some neat features that I'd like to mention here.  You can read about additional updates on the release page.

First, I've added support for resolving IDs to URLs that are stored in a database.  (See the section on URL Mapping Providers below for more information.)

You can now use xref and vref attributes on seealso tags as well; although, due to a current limitation in Sandcastle, the target attribute will be ignored.  However, if Sandcastle eventually supports the target attribute on seealso tags then my component should work without having to be rebuilt.

The editor now provides the ability to edit the base and target attributes on the component element.

In DocProject, if you configure an external mappings file then it will be included as a project item automatically.  Also, you can use the open file dialog to specify the name of a file that doesn't actually exist and it will be created when the editor is closed.  The path of the file will be made relative to DocProject's working directory.

URL Mapping Providers

The common way to define a group of mappings is to add them to the component's XML configuration using the mappings and url elements.  The class that parses url elements is, XmlUrlMappingProvider, and as you may have already guessed, it's a specialization of the abstract base class, UrlMappingProvider.  Here's what a common configuration might look like:

<component type="DaveSexton.Sandcastle.ResolveExternalLinksComponent"
    <url id="home" href="http://davesexton.com" />
    <url id="contact" href="http://davesexton.com/Contact" />

You can create your own custom providers as well.  To use them simply specify the full type name and path to the assembly using the type and assembly attributes, respectively, on the mappings element.  The outer XML of the mappings element will be supplied to your provider as raw text.

URL mapping providers can have a UI that will be displayed in the component's editor to easily modify the provider's configuration.  The built-in XmlUrlMappingProvider uses a simple DataGridView for editing the url elements.


DatabaseUrlMappingProvider is a new provider as of DocProject 1.10.1 and the ResolveExternalLinksComponent Beta 2 release.  It allows you to define ID-to-URL mappings in a database table instead of the inner XML of a mappings element.

This provider could be useful in scenarios where you already have URLs stored in a database or when you have a large number of URLs that must be shared between multiple tools, whether related to documentation or not.

A custom editor that allows you to configure the provider is embedded into the component's editor.  With it you can easily specify the data provider, connection string, query, parameters and other settings all in one place:

DatabaseUrlMappingProvider Editor

Here's an example configuration that might be used with this provider:

<component type="DaveSexton.Sandcastle.ResolveExternalLinksComponent"
  <mappings type="DaveSexton.Sandcastle.DatabaseUrlMappingProvider"
    <connection provider="System.Data.SqlClient">
      Data Source=DEV1\SQLEXPRESS;Initial Catalog=Docs;Integrated Security=True
      SELECT ID, HREF FROM UrlMappings WHERE TopicID = @TopicID;
      <parameter name="@TopicID" source="TopicID" />

The component can use any of the built-in Framework data providers (e.g, Odbc, OleDb, SqlClient) or even plug-ins such as Oracle.

Parameterized textual queries and stored procedures are supported, but only the first result set is used.  By default the provider will look for ID and HREF columns, but you can specify different names.

Parameters can have hard-coded values or they can be mapped to a specific source.  In the example above the value of the topic attribute will be used as the value for the @TopicID parameter.  The other acceptable values for the source attribute are: BaseUrl, File and Target, which also correspond to attributes on the mappings element.  To define a specific value for a parameter use the value attribute instead of source.

Pingbacks and trackbacks (2)+

Add comment