Tape: Dependency management for .NET

This is an old post and doesn't necessarily reflect my current thinking on a topic, and some links or images may not work. The text is preserved here for posterity.

Managing dependencies in .NET can be painful. You download the latest version of NServiceBus, only to find that it uses an old version of Castle Windsor, which isn't compatibile with the new version of Windsor that you need for Caliburn.

Package managers like OpenWrap and NuGet attempt to solve this problem, but they don't. Unless the NServiceBus team (or someone else) releases a new version of NServiceBus for the latest Castle Windsor, you're out of luck.

Personally, I like the way NDesk.Options goes about things. When you download the ZIP package, there's a single C# file under the ndesk-options folder that has all of the source code combined. You can just copy that C# file and paste it into your solution. No more DLL references.

Thus, I've created Tape, the solution to dependency management in .NET**.

Getting started with Tape

Let's use Tape to set up a project that depends on Autofac.

Step 1: Download tape.exe

Step 2: Download the latest Autofac source code

Step 3: Run tape over the directory containing Autofac.csproj:

The command line interface for Tape

Here I'm telling Tape to package the code in the Source\Autofac directory, packaging it into Autofac.cs. The -i switch tells tape to turn all public types into internal.

Step 4: Create a new VS project using Autofac.cs. I like to put my dependencies into a lib folder:

A VS solution with Autofac source code embedded

Step 5: Autofac source code does have a couple of MEF dependencies, so you'll need to add a reference to System.ComponentModel.Composition.

Step 6: Add some code that uses it:

interface IFoo
{
    void DoSomething();
}

public class Foo : IFoo
{
    public void DoSomething()
    {
        Console.WriteLine("Done!");
    }
}

class Program
{
    static void Main(string[] args)
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<Foo>().As<IFoo>();

        var container = builder.Build();
        var foo = container.Resolve<IFoo>();
        foo.DoSomething();

        Console.ReadKey();
    }
}

And you are done!

What it does

Tape scans the directory for all .cs files, and turns them into one big, unified .cs file. Along the way, it:

  • Removes [assembly:] attributes
  • Moves using statements into the namespace body
  • If the -i switch is passed, changes public types to internal types

What does the output look like?

Here's a couple of tapes that I taped with tape (wow, a verb AND a noun):

  1. Autofac.cs
  2. Castle.cs

Why this approach

If NServiceBus, NHibernate and Castle were available as single .cs files, making the latest version of any library work with another might be much easier.

Also, for really small libraries, it's annoying to have to reference an entire DLL. The ability to download (and embed as internal types) a single .cs file and paste it into my solution is pretty attractive.

Disclaimers

  1. Horn would probably be a better tool to use.
  2. I didn't test this on anything but Autofac and Castle
  3. It probably doesn't work on anything else
  4. There are probably a heap of edge cases it doesn't support
  5. Assemblies contain much more than just code, so yes, a lot of projects won't work with it

** probably not