如何使Simple Injector更喜欢“最派生”界面的实现?

时间:2015-07-02 13:35:02

标签: dependency-injection simple-injector

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

    <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的实现,依此类推。

为了确保可以将新的实体类型添加到系统并使用而无需创建新的具体存储库实现,我希望能够在{{1}时提供基本EntityRepository<>实现请求新实体。我知道我可以使用IEntityRepository<>方法。

我无法弄清楚的是,当请求通用存储库时,如果存在该类型的专用存储库,如何将通用存储库作为后备服务?

例如,假设我在我的应用程序中执行此操作:

RegisterOpenGeneric

大多数依赖于存储库的类都会直接请求container.Register<ICustomerRepository, CustomerRepository>(); container.RegisterOpenGeneric(typeof(IEntityRepository<>), typeof(EntityRepository<>)); 。但是,我的应用程序中可能有一个类请求基接口,如下所示:

ICustomerRepository

以上示例中发生的事情是:

  • public ContractValidator(IEntityRepository<Customer> customerRepository, IEntityRepository<Contract> contractRepository) { ... 获取customerRepository
  • 的实例
  • EntityRepository<Customer>获取contractRepository
  • 的实例

我想要发生的是:

  • EntityRepository<Contract>获取customerRepository
  • 的实例
  • CustomerRepository获取contractRepository
  • 的实例

有没有办法通知Simple Injector的解决方案,如果存在特定接口的派生,那么应该提供这个?因此,对于EntityRepository<Contract>IDerived : IBase的请求应返回IBase的实现(如果存在)。我不希望这个解决方案全面,只是为了这些存储库。可以以合理的方式完成,还是需要手动迭代IDerived谓词中的所有注册并手动检查?

2 个答案:

答案 0 :(得分:2)

假设你的课程看起来像这样

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

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

更新:使用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

答案 1 :(得分:1)

您无法使用RegisterOpenGenericRegisterManyForOpenGeneric。您将不得不手写一些代码以反映类型系统,并找到通过其特定接口注册的实现。

在我看来,你不应该有自定义存储库。这些一对一的映射会导致您的这些悲伤,此外,您通过这样做违反了SOLID原则。如果可以,请考虑here所述的设计。