In my data access layer I have a repository hierarchy that looks like this:
<TEntity>
IEntityRepository<---------+ICustomerRepository
^ ^
| |
| |
| |
+ |
<TEntity> +
EntityRepository<----------+CustomerRepository
The IEntityRepository<TEntity>
interface defines basic CRUD operations that will be useful regardless of entity type. EntityRepository<TEntity>
is a concrete implementation of these operations.
In addition, there are repository types for operations that are specific to a particular entity. In the example above, I have a Customer
entity, and the ICustomerRepository
interface defines operations such as GetByPhoneNumber
. The ICustomerRepository
also derives from IEntityRepository<Customer>
, so that the common CRUD operations will also be available for an instance of ICustomerRepository
. Finally, CustomerRepository
is the concrete implementation for the ICustomerRepository
operations, and it also inherits from EntityRepository<Customer>
for the common operations implementation.
So, going over to my actual question: I use Simple Injector to inject instances into my application. I register each of the specialized repository types in my container: CustomerRepository
as the implementation of ICustomerRepository
and so on.
To ensure new entity types can be added to the system and used without needing to create a new, concrete repository implementation as well, I would like to be able to serve the base EntityRepository<>
implementation when an IEntityRepository<>
of the new entity is requested. I've understood I can use the RegisterOpenGeneric
method for this.
What I can't figure out is, when a generic repository is requested, how can I serve the specialized repository for that type if it exists, and the generic repository only as a fallback?
For example, let's say I do this in my application:
container.Register<ICustomerRepository, CustomerRepository>();
container.RegisterOpenGeneric(typeof(IEntityRepository<>), typeof(EntityRepository<>));
Most of the classes relying on repositories would request the ICustomerRepository
directly. However, there could be a class in my application requesting the base interface, like this:
public ContractValidator(IEntityRepository<Customer> customerRepository,
IEntityRepository<Contract> contractRepository)
{
...
What happens in the example above is:
customerRepository
gets an instance of EntityRepository<Customer>
contractRepository
gets an instance of EntityRepository<Contract>
What I want to happen is:
customerRepository
gets an instance of CustomerRepository
contractRepository
gets an instance of EntityRepository<Contract>
Is there any way to inform Simple Injector's resolution that if a derivation of a particular interface exists, this should be served instead? So for IDerived : IBase
, requests for IBase
should return an implementation of IDerived
if it exists. And I don't want this resolution across the board, just for these repositories. Can it be done in a reasonable way, or would I need to manually iterate through all the registrations in the RegisterOpenGeneric
predicate and check manually?
Assuming your classes look like this
public class CustomerRepository :
ICustomerRepository,
IEntityRepository<Customer> { }
You can register all the generic implementations of IEntityRepository<>
using RegisterManyForOpenGeneric
and the fallback registration stays the same.
UPDATE: Updated with v3 syntax
// Simple Injector v3.x
container.Register<ICustomerRepository, CustomerRepository>();
container.Register(
typeof(IEntityRepository<>),
new[] { typeof(IEntityRepository<>).Assembly });
container.RegisterConditional(
typeof(IEntityRepository<>),
typeof(EntityRepository<>),
c => !c.Handled);
// Simple Injector v2.x
container.Register<ICustomerRepository, CustomerRepository>();
container.RegisterManyForOpenGeneric(
typeof(IEntityRepository<>),
new[] { typeof(IEntityRepository<>).Assembly });
container.RegisterOpenGeneric(
typeof(IEntityRepository<>),
typeof(EntityRepository<>));
But you should note that if you use any lifestyle then these separate registrations may not resolve as you would expect. This is known as a torn lifestyle.