Introduction
I decided it was time to solve a very annoying and time consuming code maintenance/quality problem when working with ASP.NET and Visual Studio, URL verification. If you're like me, you've probably had your fair share of HTTP 404s and ASP.NET maintenance troubles whenever you renamed a file in your web project and broken URLs in the code-behind pages that contain scattered uses of Response.Redirect()
similar to the following:
protected void Page_Load(object sender, EventArgs e)
{
if ( User.IsInRole( "admin" ) )
{
Response.Redirect( "~/Page2.aspx" );
}
if( User.IsInRole( "employee" ) )
{
Response.Redirect( "~/EmployeePages/ViewCustomers.aspx" );
}
if( User.IsInRole("customer") )
{
Response.Redirect( "~/CustomerPages/ViewCatalog.aspx" );
}
}
The site validation in Visual Studio somewhat is limited in its ability to validate URLs. Visual Studio validates URLs in the HTML markup view, but doesn't validate your URLs in code-behind pages. That's a bummer, because the code above is somewhat common in projects. Invalid URLs that exist in your code-behind pages are hard to debug and locate... you usually don't know when you have an invalid URL lurking in your code-behind until you hit the page with your browser. Most modern refactoring tools today do a good job refactoring code for renamed files; however, since most refactoring tools are searching for strings when renaming an ASPX file, there is a potential for error. There have been many times when refactoring didn't help me and only made the problem worse by selecting the "search string literals" option. Refactorability degrades when you start dealing with different media types in your markup and code-behind pages, such as Images, SWF, CSS, and JavaScript URLs. Media types like these go unchecked, try refactoring those in your pages! Personally, I'd love it if there was some kind of strongly typed URL Framework.
Existing Work, PageMethods
Fortunately, there has been some work in this area by Fabrice Marguerie and his open source project PageMethods.
After playing around with PageMethods, I quickly found:
- PageMethods doesn't generate URLs for pages that do not have the
[PageMethod]
attribute at the time of this writing.
- PageMethods doesn't generate URLs for media types other than ASPX pages. You can't use PageMethods URLs for JavaScript, CSS, SWF or other images.
- PageMethods is a bit slow. PageMethods added about 5 seconds to my build time with a project that had about 5 pages.
- PageMethods uses
BasePage
and Invoke(this)
in PageLoad. Although, not required, it adds to your page hierarchy.
In summary, PageMethods was too strict, and didn't work well for my purposes. I simply wanted a light-weight and fast URL framework to provide links to pages and media objects that I could use in code-behind pages and HTML markup. I also wanted to know, at compile time, the validity of all the URLs in my project. So, I decided to tackle the problem myself with a small project I created called (Strong Link), aka Slink
.
Slink
Now, let's revisit the same maintainability problem above using Slink
with strongly typed URLs:
protected void Page_Load( object sender, EventArgs e )
{
if( User.IsInRole( "admin" ) )
{
Response.Redirect( Slinks.Page2.TidleUrl );
}
if( User.IsInRole( "employee" ) )
{
Response.Redirect( Slinks.EmployeePages.ViewCustomers.TildeUrl );
}
if( User.IsInRole( "customer" ) )
{
Response.Redirect( Slinks.CustomerPages.ViewCatalog.TildeUrl );
}
}
With Slink
, not only do you get cleaner code, you get compile time checking for code-behind pages! You can also use type safe links in HTML markup:
<h4>Customer Pages</h4>
-->
<img src="~/Images/CustomerImage.jpg" alt="not strongly typed image" />
-->
<img src='<%# Slinks.Images.JPG.CustomerImage.TildeUrl %>' alt="strongly typed url" runat="server" />
-->
<asp:Image ID="imgControl" runat="server"
ImageUrl='<%# Slinks.Images.JPG.CustomerImage.TildeUrl %>' />
Yeah! And the list goes on:
- Better Maintainable Code
- Better Cleaner Code
- Better Readable Code
- Better Refactorable Code
- Better Quality Code
- Support for media types other than ASPX pages
- Fast code generation
- Non-intrusive overhead (no need to call any setup methods or expensive runtime checking)
- Use as-you-go integration (doesn't require any rewrites of your code to start using
Slink
)
- XML configuration based
- Intellisense for any of your files and media objects!
Now that's what I'm talking about. So how do you get your hands on all this Slink ASP.NET goodness? Keep reading...
Getting Started with Slink
There are two basic ways to use Slink
:
- You can use Visual Studio and an accompanying CR_Slink Plug-In (via DXCore) to automatically generate a type safe file in your projects App_Code directory.
OR
- You can use Slink.exe in a build task to generate a strongly typed safe file in your App_Code directory.
Details about how to setup each method is discussed later. First, there's some XML configuration you'll need to perform to tell Slink
what to generate. Here is a sample configuration that you place in your Web.config:
<!---->
<configuration>
<configSections>
<section name="Slink" type="Slink.Core.SlinkConfigSection,
Slink.Core"/>
</configSections>
<Slink>
<GlobalSettings EngineEnabled="True" RootNamespace="Slinks"
TypesafeGeneratedFileTarget="App_Code\Slinks.cs"/>
<NamespaceRules>
<add key="aspxFiles" Extension="*.aspx" UseFolderNamespace="True"/>
<add key="jpgFiles" Extension="*.jpg"
NamespaceOverride="Images.JPG"/>
<add key="pngFiles" Extension="*.png"
NamespaceOverride="Images.PNG"/>
</NamespaceRules>
</Slink>
<!---->
</configuration>
Below is a brief explanation of the XML configuration settings...
<section name="Slink" type="Slink.Core.SlinkConfigSection, Slink.Core"/>
- This section handler needs to be added to the
<configSections>
. The <section>
tag defines the XML node that is to be handled by the SlinkConfigSection
on behalf of the .NET configuration Framework.
<Slink>
<GlobalSettings EngineEnabled="True" RootNamespace="Slinks"/>
- The
<GlobalSettings>
node has two attributes EngineEnabled
and RootNamespace.
EngineEnabled
- Boolean - This is a project level setting. You may want to disable type safe link code generation at the project level if you are working with multiple web projects. Setting EngineEnabled="False"
disables code-generation at the web-project level.
RootNamespace
- String - Allows you to specify the Namespace for all type safe links.
TypesafeGeneratedFileTarget
- String - Allows you to specify the output file where the type safe code should be generated.
NOTE: The Web.config in this example uses the "Web Site Project" model in VS.NET 2005. If you are using the "Web Application Project" model in Visual Studio, you must remove the App_Code
from the TypesafeGeneratedFileTarget
, place it somewhere other than App_Code
. Read step 4 here to understand why you should not put code in App_Code
for "Web Application Projects". For more information on the differences between "Web Applications Projects" and "Web Site Projects", click here.
<NamespaceRules>
<add key="aspxFiles" Extension="*.aspx" UseFolderNamespace="True"/>
<add key="jpgFiles" Extension="*.jpg" NamespaceOverride="Images.JPG"/>
<add key="pngFiles" Extension="*.png" NamespaceOverride="Images.PNG"/>
</NamespaceRules>
<NamespaceRules>
are at the core of Slink
. Namespace rules dictate how type safe code is generated by the Slink
engine. Namespace rules allow you to define what type of files get type safety.
Extension
- String - If you wish to have type safe code generated for all ASPX pages, add a namespace rule with the Extension attribute set to *.aspx. Including the * (asterisk) is necessary.
UseFolderNamespace
- Boolean - Uses the file path to generate the sub-namespaces below the root namespace.
NamespaceOverride
- String - Overrides the UseFolderNamespace
attribute and allows you to define your own custom path. Your custom namespace override is appended to RootNamespace
. See the example below for JPG and PNG namespace overrides:
NOTE: If you have multiple files with the same name but a different extension, you'll want to override namespaces to avoid type collision.
Now that we have XML configuration out of the way, I'll walk you through on how to install Slink
on your machine.
Using Visual Studio Slink Plug-In
Installing VS.NET Plug-In
- Install the DXCore (free) extensibility Framework for Visual Studio from Developer Express here
- Download the
Slink
binaries Slink_bin.zip
- Copy the contents of Slink_bin.zip into your DXCore Plug-In directory
- E.g. C:\Program Files\Developer Express Inc\DXCore for Visual Studio .NET\2.0\Bin\Plugins
- Note: All contents of the Slink_bin.zip must be extracted into the plugins directory
- Register the code smith parser COM component that comes with the binary package by running the InstallSlink.bat
- Make sure your XML config section is defined in your Web.config, and start coding away!
Using Slink.exe
Now, if you're already using the Visual Studio Plug-In, there's no need to use the Slink.exe. But for those of you who don't have Visual Studio, or are using Visual Studio Express, using Slink.exe is the way to go.
Installing Slink.exe
- Download the
Slink
binaries Slink_bin.zip
- Extract the contents to the directory of your choice
- Register the code smith parser COM component that comes with the binary package by running the InstallSlink.bat
- Run the following from the command line:
- C:\Tools> slink.exe /webproject:<PathToWebProject>
- Replace
<PathToWebProject>
with the full path to your project
- Be sure to modify your Web.config and your Namespace rules are set. Otherwise, the
Slink
won't know what to do.
TIP: Unfortunately, the "Web Site Project" model in Visual Studio are handled differently than ordinary projects. With "Web Site Projects", you can't specify any "Pre-Build" event tasks. But there's an easy workaround for this limitation. If you're using Visual Studio "Web Site Project" model and you want to use the Slink.exe method for generating your type safe file, all you need to do is create a standard dummy "Class Library". Then add a pre-build task for your dummy "Class Library" and setup Slink
to execute. Once you add the dummy "Class Library" to your main web-project, your dummy "Class Library" will be built first (and slink will run) before the build / verification process takes place on your main web-project.
That all! After building the web project (if you're using the VS.NET Slink
Plug-In) or when you execute Slink.exe, you should get a file named <WebProject>\App_Code\Slinks.cs that contains your type safe URL generated code.
If you have any questions, comments, or suggestions please feel free to contact me. I hope other developers find the Slink
Framework useful. Happy coding!
Also, one last note, please use the latest binaries at CodePlex! The binaries here on Code Project may be outdated.
Slink CodePlex Project
See Brian Chavez's post on Slink.
Thoughts & Future Improvements to Slink
- Add support for strongly typed parameter passing/parsing for query string parameters
- Create a standalone
Slink
plug-in that does not rely on DXCore
- Implement more advanced code generation caching
- Possibly implement /code generate a helper method on media types such as
MyImage.GetStream()
that would automatically open and return an IO stream.
Licence
This library and code generator is licensed under the terms and conditions defined in the GNU Public License. You are free to use it for non-commercial purposes. If you are going to use any part of it in a commercial application, contact me and we can work something out. You may use the code generated output of Slink
in both commercial and non-commercial applications.
Special Thanks
Special thanks and acknowledgements go out to Philip Laureano for building his open source TaHoGen code template generator. Without it, Slink
s would not exist.
History
- Version 0.7.0.2 posted - Fixed COM Init Error, added support for Web App Projects in plug-in
- Version 0.7.0.1 posted - Bug fix in code generator, cleaned up article formatting
- Version 0.7.0.0 posted - Initial article, first draft