Register Repository Implementations Automatically with a Castle Facility

Isn’t it annoying to have to register all your repository implementations by hand? When you get to a certain number of them, it becomes quite a task. If you want to register them for multiple interfaces, it gets even worse.

When using Windsor as your inversion of control container, separating your repository registrations into a facility is a good start.  In general though, all our repositories follow a simple pattern and should be easily registrable automatically following an algorithm.

Domain driven design tells us that the repository interfaces are a part of the domain but the implementations are not.  Therefore, we usually have two assemblies that are important.

This is a typical project layout:

Our facility needs to allow us to configure what assemblies it searches for types and the base type of the repository.

Specification

Here is a simple test describing the desired behavior:


[TestFixture]
public class RepositoryRegistrationFacilityTestFixture
{
[Test]
public void ShouldRegisterCustomerRepository()
{
var kernel = new Castle.MicroKernel.DefaultKernel();

var facility = new RepositoryRegistrationFacility(typeof (IRepository<>), typeof (Customer).Assembly);

facility.Init(kernel,
new Castle.Core.Configuration.MutableConfiguration(“facility-config”));

kernel.Resolve().ShouldBeInstanceOfType(typeof (CustomerRepository));
kernel.Resolve<IRepository>().ShouldBeInstanceOfType(typeof (CustomerRepository));

}

protected interface IRepository
{
}
protected interface ICustomerRepository : IRepository
{
}
protected class CustomerRepository : ICustomerRepository
{
}
protected class Customer
{
}
}

Implementation


public class RepositoryRegistrationFacility : AbstractFacility
{
private readonly Assembly[] assemblies;
private readonly Type genericRepositoryType;

public RepositoryRegistrationFacility(Type genericRepositoryType, params Assembly[] assemblies)
{
this.assemblies = assemblies;
this.genericRepositoryType = genericRepositoryType;
}

protected override void Init()
{
Predicate isGenericRepositoryInterface =
type =>
type.GetInterfaces().Contains(
x => x.IsGenericType && x.GetGenericTypeDefinition().IsAssignableFrom(genericRepositoryType));

IEnumerabletypes =
assemblies.Aggregate(Enumerable.Empty(), (accumulator, assembly) => accumulator.Concat(assembly.GetTypes()));

IEnumerablerepositoryInterfaces = types.Where(
type => type.IsInterface && !type.IsGenericTypeDefinition && isGenericRepositoryInterface(type));

repositoryInterfaces
.ForEach(interfaceType =>
{
Type implementor =
types.FirstOrDefault(type => interfaceType.IsAssignableFrom(type) && !type.IsAbstract);
if (implementor == null) return;

IEnumerablegenericRepositoryInterfaceTypes = implementor.GetInterfaces().Where(
x =>
x.IsGenericType &&
x.GetGenericTypeDefinition().IsAssignableFrom(genericRepositoryType));

Kernel.Register(
Component.For(interfaceType).ImplementedBy(implementor).Forward(
genericRepositoryInterfaceTypes));
});
}
}

Now in fluent configuration you can just do this:

container.AddFacility("persistent-repository-facility", new RepositoryRegistrationFacility(typeof(IRepository<>), typeof(IRepository<>).Assembly, typeof(SomethingRepository).Assembly));

I have not tested the binsor registration, but you should be able to do something like this to register it using binsor:

facility RepositoryRegistrationFacility:
genericRepositoryType: IRepository
assemblies: array(System.Reflection.Assembly, IRepository.Assembly, SomethingRepository.Assembly)

I hope this is useful.

kick it on DotNetKicks.com