问题
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 ofEntityRepository<Customer>
contractRepository
gets an instance ofEntityRepository<Contract>
What I want to happen is:
customerRepository
gets an instance ofCustomerRepository
contractRepository
gets an instance ofEntityRepository<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?
回答1:
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.
回答2:
You can't use RegisterOpenGeneric
or RegisterManyForOpenGeneric
for this. You will have to hand-write some code to reflect over the type system and find the implementations to register by their specific interface.
In my opinion however you should not have custom repository. These one-to-obe mappings are cause you these grief and besides, you are violating the SOLID principles by doing so. If you can, consider a design as described here.
来源:https://stackoverflow.com/questions/31186121/how-do-i-make-simple-injector-prefer-implementations-of-the-most-derived-inter