Hawk Notes, Volume 3

I just rolled out a new feature for my blog: Series.

Back when I was working for the IronPython team, I would write series of posts on a single topic, such as writing an IronPython debugger or using IronPython's __clrtype__ metaclass feature. I've also written series on building parsers in F#, a step-by-step guide to brokered WinRT components and the home grown engine I use for this blog. And with the new job, I suspect I'll be writing even more.

So I decided to make series a first class construct in my blog. You can go to a top level page to see all my series. Or you can go to a specific series page, such as the one for Hawk Notes. One thing I really like about Series is that they display in chronological order. That makes reading a series like the IronPython Debugger much easier to follow.

Implementing Series was fairly straightforward...or at least it would have been if I hadn't decided to significantly refactor the service code. I didn't like how I was handling configuration - too much direct config reading that was better handled by options. I made some poor service design decisions that limited the use of dependency injection. Most of all, I wanted to change the way memory caching worked so that more data loaded on demand instead of ahead of time. I also took the opportunity to use newer language constructs like value tuples and local functions.

I still have two different sources for posts - the local file system and an Azure Repo. I used to use Azure Storage but I got rid of that source as part of this refactoring. I have a simple interface PostLoader1 which the AzureGitLoader and FileSystemLoader classes implement. In order to select between them at run time, I have a third PostLoader implementation named PostLoaderSelector. PostLoaderSelector chooses between the sources based on configuration and uses IServiceProvider to activate the specified type from the DI container. PostLoaderSelector gets the IServiceProvider instance via constructor injection. I couldn't find a good example of how to manually activate types from ASP.NET's DI container, so for future reference it looks like this:

public Task<IEnumerable<Post>> LoadPostsAsync()
{
    // Look Ma, a Local Function!
    PostLoader GetLoader()
    {
        switch (options.PostStorage)
        {
            case BlogOptions.PostStorageType.FileSystem:
                return serviceProvider.GetService<FileSystemLoader>();
            case BlogOptions.PostStorageType.AzureGit:
                return serviceProvider.GetService<AzureGitLoader>();
            default:
                throw new NotImplementedException();
        }
    };

    var loader = GetLoader();
    return loader.LoadPostsAsync();
}

  1. Note the lack of the "I" prefix for my interface type names. Death to this final vestigal Hungarian notation!