I know I don't exactly do much with this site anymore, but I did recently make some small changes.
Primarily, I've reorganized some of the outdated content (like the stuff related to Channel 9) so it's less prominent. I've also updated the information for Ookii.Dialogs to link to the forked project on GitHub. My own version isn't kept up to date, so most people looking for Ookii.Dialogs should probably be using that version instead (the people who are doing this are awesome, by the way, for keeping this project alive).
Oh, and the site now supports https, thanks to Azure's free SSL/TLS certificates.
Ookii.org now has a new home! It's the same site as always, except now it's hosted on Microsoft Azure. This shouldn't make any difference for you, and it probably won't mean this blog will get any more active, but all the existing content is still there.
Due to the very large volume of spam comments this site receives despite efforts on my part to block them (recaptcha is apparently completely useless), I'm unfortunately forced to disable comments on all posts on this site.
If you wish to contact me about anything pertaining to this site, please use Twitter instead.
I apologize for the inconvenience.
Today I'm releasing another small tool that I created for personal use that I believe others might find of use. The tool is the .Net Configuration Section Documentation Generator (more proof that I should not be put in charge of naming things), which does what it says on the tin: it generates documentation for configuration sections defined using .Net's ConfigurationSection
class.
Basically, it can generate an XSD schema from a ConfigurationSection
, to which you can then add annotations to document the elements and attributes of the section, and then generate an HTML documentation file from that schema. The tool can be used with Microsoft .Net and Mono.
As an example, check out the schema for the documentation generator's own configuration section as generated by the documentation generator, and with added annotations. And finally, the documentation file generated from the annotated schema. This is only a very trivial configuration section, but it gives you an idea of what the output of the documentation generator looks like.
More information and downloads here.
While working on the update to my site (which is now completely finished), I came across a minor problem: I had an old ASP.NET Web Service (you know, the .asmx type, predating even WCF) that I needed to preserve at the same URL for compatibility purposes.
However, that URL was inside a folder on the old site. And in the new ASP.NET MVC site, that folder didn't exist anymore; instead, requests for that path were handled by an MVC Controller. This meant that it was not possible to leave the .asmx file at its original location, because an existing physical directory pre-empts the MVC route for the controller, so it would render all the other pages with the same base URL unviewable.
I figured I'd be able to use RouteCollection.MapPageRoute
to redirect the old URL to the .asmx file's new location, but it turned out that this function only works for pages (.aspx files), as it requires that the HTTP handler for the route's target derives from System.Web.Page
. Searching the web wasn't able to find a solution for this, but some investigation into the inner workings of ASP.NET routing let me devise my own solution, which I thought I'd share here.
The trick, it turns out, is to create a custom IRouteHandler
, one that is able to return the correct IHttpHandler
for web services. This turned out to be fairly simple:
using System; using System.Web; using System.Web.Routing; using System.Web.Services.Protocols; public class ServiceRouteHandler : IRouteHandler { private readonly string _virtualPath; private readonly WebServiceHandlerFactory _handlerFactory = new WebServiceHandlerFactory(); public ServiceRouteHandler(string virtualPath) { if( virtualPath == null ) throw new ArgumentNullException("virtualPath"); if( !virtualPath.StartsWith("~/") ) throw new ArgumentException("Virtual path must start with ~/", "virtualPath"); _virtualPath = virtualPath; } public IHttpHandler GetHttpHandler(RequestContext requestContext) { // Note: can't pass requestContext.HttpContext as the first parameter because that's // type HttpContextBase, while GetHandler wants HttpContext. return _handlerFactory.GetHandler(HttpContext.Current, requestContext.HttpContext.Request.HttpMethod, _virtualPath, requestContext.HttpContext.Server.MapPath(_virtualPath)); } }
This route handler can then be used to add a route for a .asmx web service:
routes.Add("RouteName", new Route("path/to/your/service", new RouteValueDictionary() { { "controller", null }, { "action", null } }, new ServiceRouteHandler("~/actualservice.asmx")));
One important thing to note is the RouteValueDictionary
values I'm passing to the route. If you don't add values for "controller" and "action" to the dictionary, the Html.ActionLink helper method will match this route for any controller or action, leading to completely incorrect URLs if this is the first matching route. Since you probably want to have this route before the MVC default route (which was the case for me), that would be a problem. Setting the values in the route value dictionary resolves this issue.
Of course, you can create your own extension method to make this easier:
public static Route MapServiceRoute(this RouteCollection routes, string routeName, string url, string virtualPath) { if( routes == null ) throw new ArgumentNullException("routes"); Route route = new Route(url, new RouteValueDictionary() { { "controller", null }, { "action", null } }, new ServiceRouteHandler(virtualPath)); routes.Add(routeName, route); return route; }
Which means you can now simply use this:
routes.MapServiceRoute("RouteName", "path/to/your/service", "~/actualservice.asmx");
And voilà, I was now able to preserve the URL to my legacy web service without needing to have a physical file matching that URL. If you're faced with a similar situation, I hope this helps you.