How do I make Simple Injector prefer implementations of the “most derived” interface?

懵懂的女人 提交于 2019-12-11 12:48:52

问题


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 ICustomerRepositorydirectly. 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?


回答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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!