Self-hosting ServiceStack With Mono, NuGet, Xbuild, Linux, and a Text Editor

This post is about how to develop a self-hosting ServiceStack app on Linux, using a text editor, Mono, and xbuild.

Your first response to that may well be: “Oh god why?”

ServiceStack on Mono

Well, we use ServiceStack at work to develop our services. It’s great. I wanted to take a deeper dive into SS and have a play around at home. Linux is my primary OS at home, and as ServiceStack has first-class Mono support as a priority feature, I thought it could be a fun experiment.

While SS is developed to be able to run on Mono, I believe generally the dev experience is more that of “develop in Visual Studio, deploy to Linux server.” Which makes sense, as VS2012 is a pretty A+ development environment, especially for anything C# related. However, just to be deliberately perverse, and perhaps learn something in the process, I’m going to do the development in Linux with a text editor and xbuild.

MonoDevelop vs Text Editor

OK, so why not use MonoDevelop (or Xamarin Studio) though? Main reason — I love Vim. In Visual Studio I can use the rather amazing VsVim, but MonoDevelop’s Vim binding are not a patch on that, so I’ll be using the real deal here in Linux. Vim and tmux is my IDE.

Furthermore, having to manually fiddle around with the .csproj and .sln files is a useful learning experience, and something you usually end up doing when you start looking at building on a CI server anyway, so it’s all good experience. IDEs hide you from the details, which sometimes you want, but it can be good to take a look under the hood occasionally.

Also, what with ScriptCS coming down the pipeline, maybe it’s worth getting into the habit of coding C# in Vim…

Anyway, the whys and wherefores aside, here’s how to get it all set up.

Setup

Mono

First off, of course, we need Mono. On Ubuntu the following should do the trick:

1
$ sudo apt-get install mono-complete

Setup a solution

For now I’m using MonoDevelop to create the skeleton of a solution to work with.
I’m hoping to move to a bootstrap project using something like warmup or lad shortly. For now though:

1
$ sudo apt-get install monodevelop

Once installed, within MonoDevelop create a new Console application called HelloWorld. Once that’s done, close out of MonoDevelop as henceforth we’ll be amending the solution via our text editor.

NuGet

We’re going to install ServiceStack via NuGet, and we’re going to do it via automatic package restore.

Bootstrapping NuGet

In order to do so first we’ll need to grab a copy of NuGet.exe from codeplex.

We also need to do:

1
$ mozroots --import --sync

And get a copy of Microsoft.Build.dll from a Mono for Windows installation. This blog post has more info on this, and this blog post has a copy of Microsoft.Build.dll handily available for download.

Package restore

Next we’ll set up the .nuget package restore folder in our solution, and add the NuGet.exe and NuGet.targets file to it.

Using NuGet Without Commiting Packages describes what Visual Studio does for us when we select Enable NuGet Package Restore, and we’ll recreate that here.

Make sure Microsoft.Build.dll is in the same directory as your NuGet.exe and then run:

1
2
3
4
5
6
$ mkdir .nuget
$ mono --runtime=v4.0.30319 NuGet.exe NuGet.CommandLine
$ mono --runtime=v4.0.30319 NuGet.exe NuGet.Build
$ cp NuGet.CommandLine.{version}/tools/NuGet.exe .nuget
$ cp NuGet.Build.{version}/tools/NuGet.targets .nuget
$ cp Microsoft.Build.dll .nuget

We can add these to our solution with:

HelloWorld.sln
1
2
3
4
5
6
7
8
...
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{4251C4CB-B63F-44DA-B8B2-FE53BFB202F5}"
        ProjectSection(SolutionItems) = preProject
                .nuget\NuGet.exe = .nuget\NuGet.exe
                .nuget\NuGet.targets = .nuget\NuGet.targets
        EndProjectSection
EndProject
...

(I didn’t bother with NuGet.Config, as it just tells source control to ignore our packages folder. We can do that ourselves, say in .gitignore if we’re versioning with git.)

Next we set up the .csproj file to expect package restoration.
Add a RestorePackages tag and an import to NuGet.targets using the following:

HelloWorld/HelloWorld.csproj
1
2
3
4
  <RestorePackages>true</RestorePackages>
  ...
  <Import Condition="HasTrailingSlash('$(SolutionDir)')" Project="$(SolutionDir).nuget\nuget.targets"/>
  <Import Condition="!HasTrailingSlash('$(SolutionDir)')" Project="$(SolutionDir)\.nuget\nuget.targets"/>

(See this post and comment).

I also found that I needed to remove a trailing space in the RestoreCommand setting in NuGet.targets:

.nuget/NuGet.targets
1
2
<RestoreCommand>$(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)"  $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir "$(SolutionDir) " </RestoreCommand>
                         --------->-------------->------------>----------->--------->---------->--------->--------->--------->-------->-------->------->------->-------^ this

I also uncommented the nuget.org package source:

.nuget/NuGet.targets
1
2
3
<ItemGroup Condition=" '$(PackageSources)' == '' ">
    ...
        <PackageSource Include="https://nuget.org/api/v2/" />

Setup packages.config

Once that’s all done, we should be hot to trot and can set up a list of the packages we want. This is done in packages.config.

HelloWorld/packages.config
1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ServiceStack" version="3.9.43" targetFramework="net40" />
<package id="ServiceStack.Common" version="3.9.43" targetFramework="net40" />
<package id="ServiceStack.Logging.Log4Net" version="1.0.5.0" targetFramework="net40" />
<package id="ServiceStack.OrmLite.Sqlite.Mono" version="3.9.43" targetFramework="net40" />
<package id="ServiceStack.OrmLite.SqlServer" version="3.9.43" targetFramework="net40" />
<package id="ServiceStack.Redis" version="3.9.43" targetFramework="net40" />
<package id="ServiceStack.Text" version="3.9.43" targetFramework="net40" />
</packages>

OK, great. We also need to actually add the references to our project.

HelloWorld/HelloWorld.csproj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="ServiceStack">
        <HintPath>..\packages\ServiceStack.3.9.43\lib\net35\ServiceStack.dll</HintPath>
    </Reference>
    <Reference Include="ServiceStack.Interfaces">
        <HintPath>..\packages\ServiceStack.Common.3.9.43\lib\net35\ServiceStack.Interfaces.dll</HintPath>
    </Reference>
    <Reference Include="ServiceStack.Common">
        <HintPath>..\packages\ServiceStack.Common.3.9.43\lib\net35\ServiceStack.Common.dll</HintPath>
    </Reference>
    <Reference Include="ServiceStack.ServiceInterface">
        <HintPath>..\packages\ServiceStack.3.9.43\lib\net35\ServiceStack.ServiceInterface.dll</HintPath>
    </Reference>
    <Reference Include="ServiceStack.OrmLite.SqlServer">
        <HintPath>..\packages\ServiceStack.OrmLite.SqlServer.3.9.43\lib\ServiceStack.OrmLite.SqlServer.dll</HintPath>
    </Reference>
    <Reference Include="ServiceStack.OrmLite">
        <HintPath>..\packages\ServiceStack.OrmLite.SqlServer.3.9.43\lib\ServiceStack.OrmLite.dll</HintPath>
    </Reference>
    <Reference Include="ServiceStack.Text">
        <HintPath>..\packages\ServiceStack.Text.3.9.43\lib\net35\ServiceStack.Text.dll</HintPath>
    </Reference>
    <Reference Include="ServiceStack.Redis">
        <HintPath>..\packages\ServiceStack.Redis.4.9.43\lib\net35\ServiceStack.Redis.dll</HintPath>
    </Reference>
  </ItemGroup>
  • use some vim-fu to speed this up

And finally… the code!

As per the ServiceStack wiki, we can get going with the following code:

HelloWorld/Main.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
using System;
using ServiceStack.ServiceInterface;
using ServiceStack.WebHost.Endpoints;

class Program {

  public class Hello {
      public string Name { get; set; }
  }

  public class HelloResponse {
      public string Result { get; set; }
  }

  public class HelloService : Service
  {
      public object Any(Hello request)
      {
          return new HelloResponse { Result = "Hello, " + request.Name };
      }
  }

  //Define the Web Services AppHost
  public class AppHost : AppHostHttpListenerBase {
      public AppHost() : base("StarterTemplate HttpListener", typeof(HelloService).Assembly) { }

      public override void Configure(Funq.Container container) {
          Routes
                            .Add<Hello>("/hello")
                            .Add<Hello>("/hello/{Name}");
      }
  }

  //Run it!
  static void Main(string[] args)
  {
      var listeningOn = args.Length == 0 ? "http://*:1337/" : args[0];
      var appHost = new AppHost();
      appHost.Init();
      appHost.Start(listeningOn);

      Console.WriteLine("AppHost Created at {0}, listening on {1}", DateTime.Now, listeningOn);
      Console.ReadKey();
  }
}

Build it with xbuild:

1
$ xbuild

Run the exe:

1
$ mono bin/Debug/HelloWorld.exe

Browse to the service:

http://localhost:1337/hello/World!

Et voila!

Next steps

I’d like to look at also serving up Razor views straight from the service.

Also, as mentioned, I’m going to try and set up a bootstrappable project template with warmup/lad that avoids having to do most of the grunt-work described in this post.

Comments