How to Self-Host Nancy Without Locking Your DLLs: Shadow Copying

Reading time ~2 minutes

This is in response to a GitHub issue for Nancy. The user is trying to self-host Nancy without locking their DLLs. One easy way to do this is to create a wrapper program which runs the actual program with shadow copying assemblies. No DLLs are locked and the changes are minimal.

I will first show a simple application which self-hosts Nancy and can serve a request to "/". The program will start listening, wait for a key to be pressed and then exit. This is the code:

using System;
using Nancy;
using Nancy.Hosting.Self;

class Program {
    static void Main( string[] args ) {

        var url = new Uri( "http://localhost:12345" );
        using ( var host = new NancyHost( new DefaultNancyBootstrapper(), url ) ) {
            host.Start();

            Console.WriteLine( "Now listening, have fun!" );

            Console.ReadLine();
        }

    }
}

We will add a simple NancyModule so we can test the application at http://localhost:12345/:

using System;
using Nancy;

public class HelloWorldService : NancyModule {
    public HelloWorldService() {

        Get["/"] = x => {
            return Response.AsText( "Hello World" );
        };

    }
}

This program will work as is. The problem is that it locks any assemblies it references.

To work around this problem, add another executable to wrap the actual implementation. In order to keep the DLLs unlocked, we will run the implementation from a separate AppDomain with Shadow Copying enabled.

AppDomains are a very powerful feature of the Common Language Runtime for isolating code. They can use different security contexts, modify how assemblies are loaded and can be managed independently. There can be multiple AppDomains within a single process and can achieve some of the same isolation benefits as processes.

Using the separate AppDomain allows us to set the ShadowCopyFiles option to "true". This option will cause the assembly loading process to copy each assembly into a different directory and then load them from the new location. The local copies are left unlocked. For more information on Shadow Copying Assemblies refer to MSDN.

The whole solution would look like the diagram below:

The wrapper executable calling the actual program to run it

This is the wrapper program to call the actual executable Implementation.exe:

using System;

class Program {
    static int Main( string[] args ) {

        AppDomainSetup setup = new AppDomainSetup {
            ShadowCopyFiles = "true" // This is key
        };

        var domain = AppDomain.CreateDomain( "Real AppDomain", null, setup );

        // Execute your real application in the new app domain
        int result = domain.ExecuteAssembly(
            "Implementation.exe",
            args
        );

        return result;

    }
}

That is all there is to it. Don’t want your DLLs to be locked? The easy solution is to use another AppDomain with Shadow Copying enabled.

All the code for this blog post can be found in this sample project.

The Worst Week of My Life

In January I had the worst week of my life. My wife and I joked we wanted to start 2016 in February. Within a single week I lost my job a...… Continue reading

Vanilla JS Tetris - Good Luck, Have Fun

Published on January 31, 2016

Introduction to Dependency Injection

Published on January 08, 2016