Easy URL Rewriting With ASP.NET Routing

URL rewriting is a great way to avoid the problems typically associated with the standard .NET way of building web apps.  Take this typical URL, for example:

http://www.site.com/displaycategory.aspx?category=tools

Most ASP.NET solutions wind up having URLS that look like this.  It works, but there are problems with these types of URLs:

  • They’re long and ugly.
  • They make things look complicated and scary to typical end users.
  • They don’t let power users understand and navigate the site through modifying the URL.
  • Search engines don’t like them
  • Inner workings of your code are exposed, which could lead to security issues.
  • URLs are tied to the specific implementation

What you really want is to have that same web page handling requests, but accessed through an URL like this:

http://www.site.com/categories/tools

There are two great tools in the ASP.NET world to rewrite URLS: The IIS URL Rewrite module, and the System.Web.Routing functionality built into the framework.  Unfortunately, they both have serious drawbacks.

The URL Rewrite module snaps into IIS and lets you configure rewriting without touching any code.  It’s worth researching this tool a bit, because depending on what you’re trying to accomplish, it might suit your needs exactly.  But, it does suffer some serious drawbacks for .NET developers:

  • The URL Rewrite module must be installed on the server.
  • Visual Studio is not aware of URL Rewrite.
  • URL Rewrite doesn’t work with the ASP.NET Development Server built into Visual Studio; you have to build and debug your application through IIS.
  • You can only configure rewrites through the limited URL Rewrite scheme; you can’t write code.

ASP.NET Routing doesn’t suffer from these drawbacks, but while it’s far more powerful, it’s also far more complicated to set up.  It does require code changes, and when used in the traditional manner, it requires each page in your site to be rewritten to use the Routing architecture.  This means a lot of work.  But, there’s a very easy way to harness the power of Routing and use it much like you would the URL Rewrite module.

Enable routing for your application

The first thing you’ll need to do is map a page route within your application.  This is done through code.  The Global.asax class contains a method called Application_Start which is run whenever your application starts, so it’s an ideal place for this.  Find this method, and add this code:

// Register routing
System.Web.Routing.RouteCollection routes = System.Web.Routing.RouteTable.Routes;
routes.Ignore("{resource}.axd/{*pathInfo}");
routes.MapPageRoute("Generic Routing", "{page}/{*id}", "~/routing.aspx");

There are three important arguments to the MapPageRoute call: a friendly name for the route you’re adding (call it whatever you’d like), the format of the URL that’s going to be caught, and the path to the ASP.NET page you’d like to handle matching URLs.  In this example, we’re going to catch URLs of the style used as an example at the top of this page, but you could easily change this to work with an MVC pattern or anything else you need.  Of course, you’re not limited to just one mapping, but that’s all we need for this example.  The asterisk is used to indicate that the {id} part of the URL pattern could contain slashes. The call to .Ignore prevents requests to WebResource.axd from being caught by your routing.

Build your routing pages

Now, add a page called “routing.aspx” to your project, and add this code to Page_Load:

// Get routing data
string page = (string)this.RouteData.Values["page"];
string id = (string)this.RouteData.Values["id"];

// Transfer to appropriate page
if (page == "categories")
Server.Transfer(string.Format("~/displaycategory.aspx?category={0}", id));
else if (page == "titles")
Server.Transfer(string.Format("~/displaytitle.aspx?title={0}", id));

You can see what’s going on here: the code gathers the values that were used to built the URL, checks to see which page should handle the request, and then forwards the request on the the proper page.  In this example, we also remap requests to URLs like this:

http://www.site.com/titles/ExampleTitle

From this point forward, you can configure as many rewrites as you’d like through this one little bit of code.  Because this is code, though, you can modify this to suit whatever needs you may have.  If you wanted, you could even write this to draw data from an XML file so you don’t need to touch code to edit your URL mappings.

Really, this is the best of both worlds: easy to configure, drop-in URL rewriting that will work with any existing solution that doesn’t require anything to be installed, works with Visual Studio, and lets you write code wherever you need a bit more complexity.

Choosing Between C# and VB

Here’s a question most .NET developers have to deal with: C# or VB?

This can be a pretty heated debate; people love to defend the tools they love.  Once you get down to work, though, both languages are very similar.  They both have access to the same libraries and tools, they both have full support from Microsoft and enormous developer communities, and they both get the job done well.

But there are differences.  Let’s look at some of the more important ones:

C# Only: Better syntax

Let’s face it: C-style syntax is better than BASIC-style syntax.  You just can’t argue this one.  BASIC is too wordy; C lets you focus on what matters: your code.  Sure, both languages have code generation and IntelliSense and code snippets, and yes, you can come up with examples where VB code is shorter and more elegant than C#.  But for the most part, it’s pretty hard to argue that VB syntax is designed for experienced developers.

This isn’t as big a deal as you might think.  There’s no scenario where C# syntax is much faster to code in than VB syntax (assuming you have Visual Studio to back you up). But, C# is just a tiny bit faster in 500 different ways, and it adds up.  There are other factors to consider in choosing a language, of course, but this remains a very compelling argument.

C# Only: More advanced development community

VB is generally easier for new developers to pick up, and often allows faster development.  This might sound like an advantage to VB, but there’s a huge counter-argument: the C# community tends to be more advanced than the VB community, and is often more respected.  An experienced developer who prefers VB might have a hard time convincing others that VB can sometimes be a better choice, but a developer who only knows VB will be laughed right out of the room.

If you’re trying to decide on a single language to learn, don’t.  You need to understand at least half a dozen languages and technologies to get anything done in the real world: HTML, CSS, Java(script), XML, SQL, C(++), and more. And if you’re going to be a .NET developer, learn C# and VB.

C# Only: Unsafe code

The .NET world is wonderful, but sometimes you need to drop back to the frightening world of direct memory management.  You can usually accomplish the same tasks in VB through managed code, and even in the pre-.NET world, VB could still read and write to locations in memory directly, but there’s no getting around the fact that C# is a better choice if you can’t imagine a world without pointers.

C# Only: Checked / Unchecked

C# also lets you control exactly when overflows and underflows are caught and when they’re ignored.  In the managed world, it’s pretty tough to argue that overflow can actually be useful, but there’s a lot of legacy code – and legacy developers – out there who depend on things working they way they always have.

C# Only: Iterators

C# also lets you work with iterators.  Sure, VB knows how to iterate, but C# has a bit of extra power and flexibility here.  Check this out:

public IEnumerator<string> GetEnumerator()
{
foreach (string s in strings)
{
yield return s;
}
}

Iterators essentially let a function return values in the middle of the function.  This is a great tool, and one that’s hard to get used to not having when coding under VB.

C# Only: Refactoring

Only C# includes refactoring support build right into the IDE.  These are a collection of extra tools and commands that make development easier and faster, and C# developers are often shocked to learn that VB doesn’t include these features.  True, there are enhanced refactoring add-ins available for both languages that do a better job than what’s built into the C# IDE, but you can’t beat having something ready to go right out of the box.

VB Only: Handles and WithEvents

In C#, you have to hook up events through code.  Sure, there’s designer support available, but it makes for a more complicated project.  In VB, the Handles keyword does all this work for you.  When it comes to creating a UI for your application, this is a really big deal and makes VB developers significantly more productive: things are simpler, and you just don’t have to write as much code.  When it comes to writing business logic and other UI-less code, this doesn’t really matter very much.

VB Only: With

VB offers the With structure.  Not only is this convenient, it also improves performance.  Take this bit of C# code:

System.Text.StringBuilder sb = new StringBuilder();
sb.AppendLine("FileName: " + System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName);
sb.AppendLine("Memory size: " + System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleMemorySize.ToString());
sb.AppendLine("Entry point: " + System.Diagnostics.Process.GetCurrentProcess().MainModule.EntryPointAddress);
sb.AppendLine(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileVersionInfo.IsDebug.ToString());

And now look at it under VB:

Dim sb = New System.Text.StringBuilder
With System.Diagnostics.Process.GetCurrentProcess.MainModule
sb.AppendLine("File name: " + .FileName)
sb.AppendLine("Memory size: " + .ModuleMemorySize)
sb.AppendLine("Entry point: " + .EntryPointAddress)
sb.AppendLine("Debug: " & .FileVersionInfo.IsDebug)
End With

That’s just less code.  Less code is easier to write, read, and maintain.

This example might be a little contrived:  in the real world, you’d just declare a new variable (and give it a short name).  My point, though, is that with VB, you don’t have to do this.

VB Only: My

The My class is pure convenience.  There’s nothing under My that can’t be found elsewhere in the framework, but it makes it very easy to access a lot of calls in the framework that were previously difficult to find and use.  Have a look at this:

Try
If My.User.IsInRole(ApplicationServices.BuiltInRole.Administrator) Then
My.Computer.Network.DownloadFile("http://server.com/data.xml", "C:\")
Else
My.Computer.Network.DownloadFile("http://server.com/data.xml", My.Computer.FileSystem.SpecialDirectories.Desktop)
End If
My.Computer.Audio.PlaySystemSound(Media.SystemSounds.Beep)
Catch ex As Exception
My.Application.Log.WriteException(ex)
My.Computer.Audio.PlaySystemSound(Media.SystemSounds.Exclamation)
End Try

C# can do all this, of course, but it’s going to take more code.  That said, a lot of the functionality under My is just there to help beginners find what they’re looking for.  There are a few things here that are invaluable (such as the My.Settings class), but generally, C# developers won’t miss this too much.

VB Only: XML / Date Literals

Date literals have been around in VB forever, and while it’s debatable how often you should be hardcoding dates in code, it’s still nice to have the option (although it’s too bad the illogical American MM/dd/yyyy format is used).  XML literals, on the other hand, are a huge leap forward.  Once you work with XML in VB for a while, going back to C# will be pretty painful.  Have a look at this code, for example:

Dim allScreens = From s In Screen.AllScreens
Select <Screen>
<Device><%= s.DeviceName %></Device>
<Width><%= s.Bounds.Width %></Width>
<Height><%= s.Bounds.Height %></Height>
<BitsPerPixel><%= s.BitsPerPixel %></BitsPerPixel>
</Screen>

Dim document = <?xml version="1.0" encoding="utf-8"?>
<Screens>
<%= allScreens %>
</Screens>

document.Save("screens.xml")

That’s insanely, ridiculously simple.  And the IntelliSense support here is amazing; you really have to try it to understand how beneficial this is.  If you work with XML much, this is a really compelling reason to pick VB over C#.

VB Only: Late-binding and COM

This is another big one.  VB allows developers to use late-binding.  Essentially, this means a developer can call a member on a variable declared simply as Object.  At run-time, the compiler looks at the object, and if the call makes sense, it runs.  If it doesn’t make sense, an error occurs.  In the theoretical world of pure managed code and beautifully designed classes, using such a feature would be considered poor code.  In the real world, though, it’s nice to have this option available.  And where it really makes a world of difference is when you’re working with COM objects.  Again, let’s compare.  Here’s some VB code that automates Microsoft Word a bit:

With CreateObject("Word.Application")
With .Documents.Add()
.Range.Text = Clipboard.GetText()
.SaveAs2("clipboard.docx")
.Close()
End With
.Quit()
End With

And here’s the same code in C# (brace yourself!):

object app = Activator.CreateInstance(Type.GetTypeFromProgID("Word.Application"));
app.GetType().InvokeMember("Visible", System.Reflection.BindingFlags.SetProperty, null, app, new object[1] { true });
object docs = app.GetType().InvokeMember("Documents", System.Reflection.BindingFlags.GetProperty, null, app, null);
object doc = docs.GetType().InvokeMember("Add", System.Reflection.BindingFlags.InvokeMethod, null, docs, null);
object range = doc.GetType().InvokeMember("Range", System.Reflection.BindingFlags.InvokeMethod, null, doc, null);
range.GetType().InvokeMember("Text", System.Reflection.BindingFlags.SetProperty, null, range, new object[1] { Clipboard.GetText() });
doc.GetType().InvokeMember("SaveAs2", System.Reflection.BindingFlags.InvokeMethod, null, doc, new object[1] { "clipboard.docx" });
doc.GetType().InvokeMember("Close", System.Reflection.BindingFlags.InvokeMethod, null, doc, null);
app.GetType().InvokeMember("Quit", System.Reflection.BindingFlags.InvokeMethod, null, app, null);

As you can see, working with COM in this fashion is really, really painful under C#.  In fact, only through reflection is this even possible!  This has been improved somewhat with the recent addition of the dynamic type in C# 4.0, if you’re able to take advantage of the latest version.

VB Only: Implicit Conversions

In C#, all type conversions must be performed explicitly.  In VB, most simple conversions are performed automatically by the compiler.  This means you can add 2 and 2.0, and it means you don’t need to type .ToString() anywhere near as often.  It can save a lot of time, but can also cause bugs if the conversion wasn’t expected.  Note that you don’t want to give this feature to new developers; they will only get themselves into trouble with it.  It’s great to have conversions done implicitly, but only if you already understand what’s going on under the hood.

VB Only: Better IntelliSense and Error List

In VB, the IDE is much faster at updating IntelliSense, the Error list, and other tools.  Under C#, you often need to rebuild your project to update the Error list and certain other features.  And, IntelliSense is just all-around better under VB.  This might not seem like a drastic difference, but it saves you a second or two countless times a day.  You’ll certainly notice this when moving between the languages frequently.

Other Differences

I think that’s about it for major features available in only one language.  There are a plethora of other small arguments to make, but none that really have much of an overall impact on choosing a language.  There are, of course, many other major differences that don’t really have a clear advantage one way or the other.  Namespaces are handled differently.  VB offers project-level Imports, while C# is better at helping you manage file-level ‘using’ statements.  C# offers static classes, while VB offers Modules.  Commenting works differently.

Recommendations

There is one area where C# is really the only sensible choice:

  • Unsafe code

There are three areas where VB has a clear advantage over C#:

  • Working with COM (although C# 4.0 narrows the gap)
  • Working with XML
  • Developing UI

Outside of these areas, it all comes down to personal preference.  C# has the better syntax and a more advanced community, while VB offers a range of features and aids not available to C#.

But remember: it’s not about the tool.  It’s about what you do with it.

OneNote not syncing–Windows Phone 7

It’s finally here, and it seems that it is getting better and better each and every day! While the new Windows Phone 7 does follow an Apple-like model by excessively locking down the device, it does seem that underneath all of that new shininess is the ability to get under the hood and tweak the device just like any other Windows based device ever created.

Overall, I love my LG Optimus Quantum, but I do have to report one small glitch that I have encountered and give our readers some pointers on how to fix this.

LG_Quantum_Windows_Phone_721

One of the first things I noticed on the device was the beautifully integrated Office 2010 components that can be set to automatically sync to the Windows Sky Drive. I have always loved OneNote and to have a fairly complete version of this running on a smart phone is a god send.  Unfortunately, it isn’t as intuitive as it may look.

By default, when you set up a Windows Phone 7, it asks you for a live ID to which it will sync itself up to. With OneNote, it will create a default OneNote notebook called Personal (Web) . Logging in to your Skydrive, you will also see this notebook.

I wasn’t a fan of this as I like to create custom notebooks and I really didn’t want my default location for my saved noted to go to Personal (Web) (whoever chose this name for the default notebook should be tarred and feathered and or sent to Guantanamo). I proceeded to delete that notebook. It is at this point that my brand new phone started generating errors and barking at me saying it couldn’t sync, and that the default location for saving unfiled notes was gone. The phone gave the helpful suggestion of creating a new location for these, however, there seemed to be no apparent way of creating a new notebook that would sync and allow itself to become the default.  The obvious solution would be to log into the Skydrive account and create a new notebook called Personal (web) and everything would be happy again..? Right? Well, no , not exactly.  I struggled for a long time and was almost ready to wipe the phone when I decided to browse to office.live.com on the phones browser.  What I saw there was actuallysome other notebooks that I thought I was having trouble syncing.  If you open one of these from here, they will automatically be added to your phone and you can then select one of these as your default notebook. This will entirely fix the sync issue.

This is really counter-intuitive and Microsoft doesn’t seem to tell anyone that they have to do this anywhere. Instead on their Windows Phone 7 site, they warn people about deleting the default with no mention of what to do if you have done this.  This is also the method you will need to use to add your own existing OneNote notebooks to the phone.

Again, I love the phone, but Microsoft will hopefully start making things like this a little clearer. It’s not that any of this was difficult, it’s just that it was by complete happenstance that I found the answer to my problem. 

Hope this helps someone out there.

Speed up the Visual Studio Development Web Server

Here’s a fix to a problem many people don’t realize they’re having!  When you debug a web site project in Visual Studio, by default, a simple little web server called ‘Visual Studio Development Web Server’ (previously known as Cassini) fires up so you can test your site with whatever browser or tools you want:

image

You’d think that since this tiny little web server runs on the local machine, everything should be pretty speedy, right?

Well, it’s not.  Sometimes, it kind of works.  Sometimes, it times out.  And here’s the problem: IPv6.  I hate this technology.  Sure, it might be necessary, but it’s a pain-in-the-ass over-engineered solution that is going to cause everybody a LOT of grief over the next few years. By default, Windows tries to use IPv6 first.  Why?  Because it’s so much more awesome, I guess.  Unfortunately, the web server built into Visual Studio doesn’t play nice with IPv6.

I offer you several fixes here.  Pick the one you hate least!  Note that these fixes can on occasion be a bit finicky; you may have to restart your browsers, flush DNS caches, restart your computer, or scream and curse for a while.

The Quick Fix

When you start your project, your browser will be sent to an address like this (the port number will be random):

http://localhost:1805/

Simply change localhost to 127.0.0.1, so the address looks something like this (leaving the original port number):

http://127.0.0.1:1805/

Remember to include the ‘http://’ in the URL you type here.  Ugly, yes, but it works instantly, you don’t need to reconfigure anything, and you don’t need admin access.  The down side is that you have to do this every time you launch the project.

The Easy Fix

Want to fix this issue permanently?  The best way is to edit your hosts file, which you’ll find here:

%WINDIR%\System32\drivers\etc\hosts

Towards the bottom, you’ll find this line:

#    127.0.0.1       localhost

Uncomment this line by removing the ‘#’.  Then save the file.

There’s another line right after this that mentions ‘::1’; leave this one the way it is.  This file is protected, so the easiest way to save it is to save a copy to your desktop and then move this copy to the original location; this way Windows offers you the opportunity elevate and overwrite rather than simply give you a ‘read only’ error.

This fix should instantly take care of the problem machine-wide.  In theory, this shouldn’t break anything – IPv6 is still turned on, and resolution still works – but if this is a server, you might want to test things through.

The Browser-Specific Fixes

There are options within some browsers to disable IPv6.  Doesn’t seem like the best way of going about solving this problem, but hey, you do what you gotta do.

In Firefox, browse to about:config and toggle the network.dns.disableIPv6 preference:

image

In Chrome, start the browser with the “--disable-ipv6” argument.  Note that the dashes are a bit awkward; you have to get this exactly right.  See our (outdated) article Google Chrome on Windows 7 for more details on making this change in your shortcuts.

Other Fixes

There are other ways of fixing this out there.  These include various ways of disabling IPv6, registry hacks, and editing the web.config file.  None of these are particularly ideal, unless you know exactly what you’re doing (in which case, why are you reading this?).  Note that disabling IPv6 (as some existing articles out there will tell you to do) will break things!


Copyright © 2010 Paul Guenette and Matthew Sleno.