如何让 Simple Injector 更喜欢 "most derived" 接口的实现?

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

在我的数据访问层中,我有一个如下所示的存储库层次结构:

    <TEntity>                                 
IEntityRepository<---------+ICustomerRepository
        ^                            ^        
        |                            |        
        |                            |        
        |                            |        
        +                            |        
    <TEntity>                        +        
 EntityRepository<----------+CustomerRepository

IEntityRepository<TEntity> 接口定义了基本的 CRUD 操作,无论实体类型如何,这些操作都是有用的。 EntityRepository<TEntity>就是这些操作的具体实现。

此外,还有特定于特定实体的操作的存储库类型。在上面的例子中,我有一个Customer实体,ICustomerRepository接口定义了GetByPhoneNumber等操作。 ICustomerRepository 也派生自 IEntityRepository<Customer>,因此常见的 CRUD 操作也可用于 ICustomerRepository 的实例。最后,CustomerRepositoryICustomerRepository操作的具体实现,也是继承自EntityRepository<Customer>的普通操作实现。

所以,回到我的实际问题:我使用 Simple Injector 将实例注入我的应用程序。我在我的容器中注册了每个专门的存储库类型:CustomerRepository 作为 ICustomerRepository 的实现等等。

为了确保新的实体类型可以添加到系统中并在不需要创建新的具体存储库实现的情况下使用,我希望能够在 EntityRepository<> 实现 IEntityRepository<> 请求新实体。我知道我可以为此使用 RegisterOpenGeneric 方法。

我想不通的是,当请求通用存储库时,我如何为该类型提供专用存储库(如果存在),而通用存储库仅作为后备?

例如,假设我在我的应用程序中这样做:

container.Register<ICustomerRepository, CustomerRepository>();
container.RegisterOpenGeneric(typeof(IEntityRepository<>), typeof(EntityRepository<>));

大多数依赖存储库的 classes 会直接请求 ICustomerRepository。但是,我的应用程序中可能有一个 class 请求基本接口,如下所示:

public ContractValidator(IEntityRepository<Customer> customerRepository,
                         IEntityRepository<Contract> contractRepository)
{
    ...

上例中发生的情况是:

我想要发生的是:

有没有什么方法可以通知 Simple Injector 的解决方案,如果存在特定接口的派生,则应该改为提供该接口?因此,对于 IDerived : IBase,对 IBase 的请求应该 return 一个 IDerived 的实现(如果存在)。而且我不希望全面解决这个问题,只是为了这些存储库。能以合理的方式完成吗,还是我需要手动遍历 RegisterOpenGeneric 谓词中的所有注册并手动检查?

假设您的 类 看起来像这样

public class CustomerRepository : 
    ICustomerRepository, 
    IEntityRepository<Customer> { }

您可以使用 RegisterManyForOpenGeneric 注册 IEntityRepository<> 的所有通用实现,后备注册保持不变。

更新:使用 v3 语法更新

// 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<>));

但您应该注意,如果您使用任何生活方式,那么这些单独的注册可能不会像您预期的那样解决。这被称为 torn lifestyle.

您不能为此使用 RegisterOpenGenericRegisterManyForOpenGeneric。您将不得不手写一些代码来反映类型系统并找到要通过其特定接口注册的实现。

但我认为您不应该拥有自定义存储库。这些 one-to-obe 映射会让您感到悲伤,此外,您这样做还违反了 SOLID 原则。如果可以,请考虑here.

所述的设计