Question

Is it difficult in simple injector to auto register these classes:

public class RepositoryA : Repository<Hammers>, IRepositoryA
{
    ... implementing code here ...
}

public interface IRepositoryA: IRepository<Hammers>
{ //no methods
}


public class RepositoryB : Repository<Knives>, IRepositoryB
{
    ... implementing code here ...
}

public interface IRepositoryB: IRepository<Knives>
{ //no methods
}


public class RepositoryC : Repository<Swords>, IRepositoryC
{
    ... implementing code here ...
}


public interface IRepositoryC: IRepository<Swords>
{ //no methods
}

I was following the documentation described here: Simple Injector - Batch Registration

But it seems that it doesn't work if you try to inherit from another class and its interface. I can't separate the interfaces, meaning, it gets Repository and IRespositoryX, where X is the letter for the repository (A, B, C, etc). It essentially throws an error:

Sequence contains more than one element

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.InvalidOperationException: Sequence contains more than one element

It works if I manually register each class, one by one:

container.Register<IRepositoryA, RepositoryA>();
container.Register<IRepositoryB, RepositoryB>();
container.Register<IRepositoryC, RepositoryC>();
/* add more here */

Here is what I have so far:

var repositoryAssembly = typeof(StateRepository).Assembly;

var registrations = 
    from type in repositoryAssembly.GetExportedTypes()
    where type.Namespace == "MappingTool.Repository"
    where type.GetInterfaces().Any()
    where !type.IsAbstract
    select new
    {
        Service = type.GetInterfaces().Single(),
        Implementation = type
    };

foreach (var reg in registrations)
{
    container.Register(reg.Service, reg.Implementation, Lifestyle.Transient);
}
Was it helpful?

Solution

The use case that Simple Injector supports is to batch register a set of implementations by their related generic interfaces. In other words, by doing this:

container.RegisterManyForOpenGeneric(
    typeof(IRepository<>), 
    typeof(StateRepository).Assembly);

You are basically registering a closed version of the IRepository<T> interface as service, with its corresponding implementation. In other words, this will be equivalent to doing the following:

container.Register<IRepository<A>, RepositoryA>();
container.Register<IRepository<B>, RepositoryB>();
container.Register<IRepository<C>, RepositoryC>();
/* add more here */

Since you are not implementing the common use case, you will have to fall back to doing this manually. You can do this by writing the LINQ query as you already did, or you can make use of the OpenGenericBatchRegistrationExtensions.GetTypesToRegister method to make this easier:

var repositoryTypes = OpenGenericBatchRegistrationExtensions.GetTypesToRegister(
    container, typeof(IRepository<>), repositoryAssembly);

foreach (Type implementationType in repositoryTypes)
{
    Type serviceType = 
        implementationType.GetInterfaces().Where(i => !i.IsGenericType).Single();

    container.Register(serviceType, implementationType, Lifestyle.Transient);
}

Do make sure though that you understand that you are violating the SOLID principles by implementing custom interfaces on your repositories. It be beneficial to prevent such violation. Take a look at this article for more information about this.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top