Monthly Archives: June 2011

On the bus with MassTransit #1

This is the first post in a series of posts on the open source service bus implementation, MassTransit. In order to push myself through learning it, I will do these posts as I go and document stuff I learn on the way.

This first post will be about one thing, that you’ll probably end up doing quite a few times while building your awesome messaging-based distributed system: Creating a service.

Check out TopShelf and build it

I’ve described how to use TopShelf before, but a lot has changed since then. TopShelf, at the time of writing, is now in version 2.2.1.0, and since 2.0 it has a feature called “shelving”, which is the ability to host any number of services beneath one Windows Service – just like IIS hosts web apps. This is pretty awesome, but today I’ll just show “the old API”, which IMO is still pretty slick!

First, make sure you have a working Ruby installation with Albacore (gem install albacore). You’ll need these to build Topshelf.

Next, git clone git://github.com/phatboyg/Topshelf.git and build.bat. That should leave a bunch of binaries in the NET35 and NET40 folders beneath build_output. This is open source, baby!

Put your app on the TopShelf

Create a new “Console Application” and make sure to target the full .NET 4 framework (and not that bastard .NET 4 Client Profile thingie, which is default…)

Now add a reference to Topshelf.dll and log4net.dll. Punch in something like this:

class Program
{
    static void Main()
    {
        HostFactory.Run(x =>
        {
            x.SetDescription("MyBackend-description");
            x.SetDisplayName("MyBackend-display-name");
            x.SetServiceName("MyBackend-service-name");
        });
    }
}

This is all the necessary stuff to create a fully functional program, that can be run and debugged by running the resulting .exe – either from the command line, or with F5 inside Visual Studio.

Moreover, it can be installed as a Windows Service by going to the command line (as an administrator!) and executing NameOfYourApp.exe install. On my machine, I got this output:

C:\temp\TransitDemo\Backend\bin\Debug>Backend.exe  install
 
Running a transacted installation.
 
Beginning the Install phase of the installation.
Installing service MyBackend-service-name..
Service MyBackend-service-name has been successfully installed.
Creating EventLog source MyBackend-service-name in log Application...
 
The Install phase  completed successfully, and the Commit phase is beginning.
 
The Commit phase completed successfully.
 
The transacted install has completed.

Now I can net start MyBackend-service-name and net stop MyBackend-service-name on the command line, or I can go to Windows’ service control snap-in and pull the usual levers to control it. Nifty!

Now I have a perfectly functioning Windows Service that does nothing at all! Let’s output some stuff to see where we’ll put our code in a short while. Try extending the x => { ... } lambda to make the program look like this:

class Program
{
    static void Main()
    {
        HostFactory.Run(x =>
        {
            x.Service<MyBackend>(c =>
            {
                c.SetServiceName("MyBackend");
                c.ConstructUsing(() => new MyBackend());
                c.WhenStarted(d => d.StartMyBackend());
                c.WhenStopped(d => d.StopMyBackend());
            });
 
            x.SetDescription("MyBackend-description");
            x.SetDisplayName("MyBackend-display-name");
            x.SetServiceName("MyBackend-service-name");
        });
    }
}

and a class that looks something like this:

class MyBackend
{
    public void StartMyBackend()
    {
        Console.WriteLine("Started!");
    }
 
    public void StopMyBackend()
    {
        Console.WriteLine("Stopped!");
    }
}

Now, when I press F5, wait a little, and press Ctrl + C, I get this output on the console:

Started!
Stopped!
Press any key to continue . . .

Awesome, huh?

I really like how TopShelf now uses a Func<TService> to construct its service instance via ConstructUsing, instead of depending on the common service locator as the old TopShelf did. This way, I can pull the instance from an IoC container if I want.

In the next post I will take a look at how to send a message from a web application when someone tries to register as a user.