Modifying and extending Topshelf

Saturday, 17 August 2019

Topshelf is a useful library if you have a .NET application that you would like to easily turn into a Windows Service.

A common approach is to create a console application, wire in Topshelf and then you can run it as either a console app or install and start it as a Windows service.

One thing I encountered this week was that by default Topshelf changes the current directory to the location of your .exe when your application runs. This makes sense because otherwise the default current directory for a Windows service is actually c:\windows\system32! If you’re trying to load additional files from the same directory as the .exe then that’s not where you’d want to be looking so Topshelf’s alteration makes sense.

But what about for debugging?

In Visual Studio, when you press F5 to debug a web project (which is the kind I was working with), the current directory defaults to the project’s directory. Again you may have logic that makes assumptions about other resources that need to be loaded relative to the project directory - if you’re now using Topshelf that’s going to break as the current directory will be changed. How to fix this?

Stepping through the Topshelf source code, I realised that I wanted a version of ConsoleRunHost that didn’t have the calls to Directory.SetCurrentDirectory(). That class is instantiated by the RunBuilder class, which in turn is returned by the DefaultHostBuilderFactory method in HostConfiguratorImpl.

So that’s the default factory, but the same class also provides a UseHostBuilder method that allows you to provide your own factory instead. That’s just what I needed.

By adding my own factory method that returned an instance of my own custom RunBuilder, which then returned a custom ConsoleRunHost implementation that was identical to Topshelf’s ConsoleRunHost with the exception that it didn’t change the default directory.

private static HostBuilder HostBuilderFactory(HostEnvironment environment, HostSettings settings)
{
    return new MyRunBuilder(environment, settings);
}

And tell Topshelf to use this method by calling UseHostBuilder:

var rc = HostFactory.Run(x =>
{
    x.UseHostBuilder(HostBuilderFactory);
    ...
}

If you wanted to, you could even check if you were running under the debugger to decide whether to use the default or custom behaviour.

It’s nice when libraries have thought about extensibility and provide hooks for consumers to swap in custom logic.

Game over

Sunday, 11 August 2019

This year I decided to return to playing basketball socially after a break of about 5-6 years. Like most other sports I’d tried, I didn’t have much natural talent, but enjoyed participating.

This season did not start out well. The first game back, I was enjoying being back on the court (if also realising how unfit I was!). But as the match progressed, my heel was getting sorer and sorer. Later diagnosed by the Physio as tendonitis - probably caused by going out way too hard after such a long time of not running at all.

A week off and some daily stretching (that I’m still doing months later) and I was feeling ok to get back again. A few more games and then more problems..

My back had been feeling a little uncomfortable earlier in the day, but I figured lots of stretching and I’d be fine. I played with out any issues and then drove home. But as I got out of the car, I felt my back go!

Some Chiro on the Monday, more stretching and trying to stand (instead of sitting down) at work meant, after another week off, I thought I’d be ok to come back again. I played last week without incident, though I was a little sore the next day.

Yesterday, my back was still not 100% but I figured I’d see how I went. That was probably a mistake. Part-way through the second half of the game, I went to chase the ball and felt my back go again. I took no further part in the game. Time to listen to my body and give it a proper rest.

And that’s how my 2019 basketball season unceremoniously ended. Maybe I can give it another go in 2020, but if I do I think I’ll need to invest in a lot better preparation. It would be disappointing if it turns out that I can’t play again, but that’s also a possibility. And better to retire my basketball boots and still be able to do other things, than really injure myself badly.

Azure DevOps - Default permissions for force push on a branch

Monday, 8 July 2019

This error caught me by surprise today:

C:\dev\git\project [feature ↓1 ↑2 +1 ~0 -0 !]> git push --force-with-lease
Enumerating objects: 22, done.
Counting objects: 100% (22/22), done.
Delta compression using up to 8 threads
Compressing objects: 100% (5/5), done.
Writing objects: 100% (12/12), 1.61 KiB | 824.00 KiB/s, done.
Total 12 (delta 6), reused 8 (delta 4)
remote: Analyzing objects... (12/12) (8 ms)
remote: Storing packfile... done (115 ms)
remote: Storing index... done (81 ms)
To https://mytenancy.visualstudio.com/repo/_git/project
 ! [remote rejected]       feature -> feature (TF401027: You need the Git 'ForcePush' permission to perform this action. Details: identity '93086622-abb9-4886-b994-502e7f2afc21\[email protected]', scope 'branch'.)
error: failed to push some refs to 'https://mytenancy.visualstudio.com/repo/_git/project'

I was trying to push a change to a Git branch in Azure DevOps Repos. I’d just done an interactive rebase to update history on the branch and was trying to force push my changes - something I’ve done countless times before.

Reviewing the permissions for this repository, the Force push (rewrite history, delete branches and tags) permission was Not Set (eg. neither explicitly allowed nor denied). Checking other projects and repositories, this was also the case, so what’s different?

Well one thing that’s different is that I didn’t create this branch - it was created by another developer. Force pushing to a shared branch is generally frowned upon (as if it isn’t coordinated it can cause all kinds of problems), and so it seems DevOps helps guard against this problem by defaulting to granting the Force Push permission just to the branch creator (and also to users who are the Project administrator - as set in the Project details page of Project Settings).

Azure DevOps Branch user permissions

Because I didn’t create the original branch and I was not a project administrator in this particular project, no push for me!

Check out the descriptions of the different permissions for Git repositories and the default Git repository and branch permissions.

In the garden - Winter 2019

Saturday, 6 July 2019

It’s been a while since my last garden update. Things are cooling down in Adelaide. Not to the extent of other places that get regular snow and ice, but still chilly enough.

Jonquils flowering

Our apple trees are starting to mature now and we’ve had good crops from both trees this year. The Pink Lady still has fruit hanging on the tree, and they’ve coloured up really nicely and the family are enjoying eating them.

Pink Lady apple tree bearing fruit

The mandarin is doing a great job. The fruit are so much tastier than the ones you buy in the shop. So far this year I have had no problems with possums/rodents eating the fruit, so I haven’t worried about netting the tree. The fruit might be a little smaller than previous years, possibly due to an incredibly dry January/February. Some late rains came just in time.

Mandarin tree with fruit

The lemonade just keeps powering on. Such a productive tree. I’ve had to give it a few haircuts as it’s really growing tall.

Lemonade tree with fruit

Narelle did a great job planting out some potted colour a few weeks ago, and we’re continuing to enjoy the results.

Dianthus flowering in a pot

And we got to enjoy some late flowers from this rose. Very pretty.

Orange rose in flower

Time to get back in the garden and do some weeding!

Congratulations 2019-2020 Microsoft MVP!

Tuesday, 2 July 2019

I received a really nice email last night:

MVP Letter

Great to be a Microsoft MVP for another year!

I first received the Microsoft MVP award in October 2015 (which happens to be about 14 months after I took over organising the Adelaide .NET User Group). Each year since then, I wonder if I’ll be renewed. So far, so good.

It’s a nice way for Microsoft to show their appreciation for the various things I do for the developer community - running the user group, speaking, and lots of open source contributions. It’s also been a great avenue for meeting new people, visiting new places and also providing feedback direct to Microsoft’s product teams.

It’s also something I’d love more people being recognised for (especially in Adelaide). If you are doing things in the development or IT community (or know someone who is), then let me know in the comments - I’d love to nominate them.

MVP Logo