Avoiding Infrastructure References with LightInject

I recently had a read through Steve Smith's excellent blog post Avoid Referencing Infrastructure in Visual Studio Solutions, and found it quite helpful. The pattern he used of having everything referencing Core, and avoiding "unnecessary" infrastructure/implementation project references, is one I use regulary in both my work and personal projects. Not being a user of StructureMap myself, the code examples were quite interesting since they looked very similar to how you would achieve the same thing in my IoC container of choice: LightInject.

If you haven't read Steve's post yet, go read it!. All the examples below will assume the same UI-Core-Infrastructure design as he outlines.

Overview

Today, I'm going to be showing how to implement the same basic pattern using LightInject. Just like StructureMap, LightInject supports scanning assemblies by name, and just like StructureMap, it supports a single container type for registrations, but instead of "registries", they are called a composition root. When LightInject scans an assembly, it automatically looks for any type implementing the ICompositionRoot interface and will automatically run that type to register any services in it.

Note that if you use the composition root approach, only types in the composition root will be registered.

LightInject registration

So to scan an assembly in your startup code (in the UI project):

var container = new ServiceContainer();
container.RegisterAssembly("Infrastructure*"); //LightInject even supports pattern matching!
container.RegisterAssembly(typeof(SomeUIType).Assembly);

Now, in your Infrastructure project, just add an ICompositionRoot implementation:

public class InfrastructureCompositionRoot : ICompositionRoot
{
	public void Compose(IServiceRegistry serviceRegistry)
	{
		//to use Steve's example:
		serviceRegistry.Register<IGuestbookRepository, InMemoryGuestbookRepository>(); 
		// register other services here...
	}
}

LightInject will pick this composition root up when it scans the Infrastructure assembly, and will run the Compose method with the 'active' registry/container, registering all your types, no reference needed!

Copy Infrastructure assembly

Now, personally, I dislike/loathe/hate post-build steps so prefer to avoid them when I can. In this case, we can!

The first option is to manually edit your csproj as outlined in this post to build and copy the assembly without actually referencing it. Add the following to your UI project's .csproj file, substituting as appropriate:

<ProjectReference Include="..\Infrastructure\Infrastructure.csproj">
	<Project>{b402782f-de0a-41fa-b364-60612a786fb2}</Project> <!-- ‹ update this -->
	<Name>Infrastructure</Name>
	<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
	<OutputItemType>Content</OutputItemType>
	<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</ProjectReference>

and your Infrastructure assembly should be copied to the UI project's output when you build it. Alternatively, for me, I'm usually using Cake to build my project anyway so I can quickly add the below (updating as appropriate) right after my call to MSBuild or before I package/publish my app:

CopyFiles("./src/Infrastructure/bin/" + configuration + "Infrastructure*.dll", "./src/UI/bin/" + configuration);
// you can use the same pattern as you gave LightInject too!  ^

Really, copying assemblies about is pretty straightforward stuff, so use the tool you like!

Summary

Using these techniques, you can achieve the same neat decoupling of having the concrete types available to your application without needing to actually reference the project, simplifying your reference chain and enforcing clean separation.