Vaccano Vaccano - 17 days ago 7
C# Question

Resolve more than one object with the same class and interface with Simple Injector

I am trying to migrate from Unity to Simple Injector in my new project. It is so much faster than Unity that I have to give it a shot. I have had a few bumps, but nothing I could not work around. But I have hit another one with "Look-up by Key"

I have read this where the creater of simple injector states his belief that you should not need to resolve more than one class per interface.

I must be a poor programmer, because I have done that with Unity (which supports it very well) and want to do it in my current project.

My scenario is that I have an IRepository interface. I have two separate repositories I want to abstract using the IRepository interface. Like this:

container.Register<FirstData>(() => new FirstData());
container.Register<IRepository>(
() => new GenericRepository(container.GetInstance<FirstData>()));


container.Register<SecondEntities>(() => new SecondEntities());
container.Register<IRepository>(
() => new GenericRepository(container.GetInstance<SecondData>()));


IRepository/GenericRepository is a fairly common abstraction, but you can only have one in SimpleInjector

In Unity I could register both of my repositories and then setup my constructor injection to inject the instance that I needed. This was accomplished using a key for the instance. (I did not need to do a
Resolve
call in my normal code nor add a dependency to Unity outside my setup.)

With simple injector this does not work. But, for better or worse, the owner of Simple Injector thinks this feature is a bad idea.

NOTE: The author's "in app" system looks like it uses a string key for look-up, but it still requires a different class each time (
DefaultRequestHandler
,
OrdersRequestHandler
and
CustomersRequestHandler
). I just have a
GenericRepostory
which allows me to abstract my repository methods regardless of what I am connected to.


I suppose I could inherit my
GenericRepostory
for each time I want to instantiate it. Or have it take a random type parameter that I don't need. But that muddles my design, so I am hoping for another way to do it.

So are there any work arounds that don't have me creating bogus types to differentiate between my two IRepository/GenericRepository instances?

Answer

We ended up changing our Generic Repository to look like this:

/// The Type parameter has no funcionality within the repository, 
/// it is only there to help us differentiate when registering 
/// and resolving different repositories with Simple Injector.
public class GenericRepository<TDummyTypeForSimpleInjector> : IRepository

(We added a type parameter to it).
We then created two dummy classes like this (I changed the names of the classes to match my example):

// These are just dummy classes that are used to help 
// register and resolve GenericRepositories with Simple Injector.
public class FirstDataSelector { }
public class SecondDataSelector { }

Then I can register them like this:

container.Register<FirstData>(() => new FirstData());
container.Register(() => new GenericRepository<FirstDataSelector>
                   (container.GetInstance<FirstData>()));


container.Register<SecondEntities>(() => new SecondEntities());
container.Register(() => new GenericRepository<SecondDataSelector>
                   (container.GetInstance<SecondData>()));

(Note the generic type param on the GenericRepository and that I do not register it as an IRepository. Those two changes are essential to making this work.)

This works fine. And I am then able to use that registration in the constructor injection of my business logic.

container.Register<IFirstBusiness>(() => new FirstBusiness
               (container.GetInstance<GenericRepository<FirstDataSelector>>()));
container.Register<ISecondBusiness>(() => new SecondBusiness
               (container.GetInstance<GenericRepository<SecondDataSelector>>()));

Since my Business classes take an IRepository it works fine and does not expose the IOC container or the implementation of my repository to the business classes.

I am basically using the Type parameter as Key for lookup. (A hack I know, but I have limited choices.)

It is kind of disappointing to have to add dummy classes to my design, but our team decided that the drawback was worth it rather than abandoning Simple Injector and going back to Unity.