使用简单注入器解析具有相同类和接口的多个对象
本文关键字:接口 同类 对象 简单 注入器 | 更新日期: 2023-09-27 18:17:06
我正试图在我的新项目中从Unity迁移到简单注入器。它比Unity快得多,所以我不得不尝试一下。我有过一些挫折,但没有什么是我无法解决的。但是我点击了另一个"按键查找"
我读过一篇简单注入器创建者的文章,他认为每个接口不需要解析多个类。
我一定是一个糟糕的程序员,因为我用Unity(它支持得很好)做到了这一点,并想在我当前的项目中做到这一点。
我的场景是我有一个IRepository接口。我有两个独立的存储库,我想使用IRepository接口进行抽象。这样的:
container.Register<FirstData>(() => new FirstData());
container.Register<IRepository>(
() => new GenericRepository(container.GetInstance<FirstData>()));
container.Register<SecondEntities>(() => new SecondEntities());
container.Register<IRepository>(
() => new GenericRepository(container.GetInstance<SecondData>()));
IRepository/GenericRepository是一个相当常见的抽象,但是你只能在SimpleInjector
中拥有一个。在Unity中,我可以注册两个存储库,然后设置构造函数注入来注入我需要的实例。这是使用实例的一个键来完成的。(我不需要在我的正常代码中做Resolve
调用,也不需要在我的设置之外添加对Unity的依赖)
对于简单的注入器,这不起作用。但是,不管是好是坏,Simple Injector的所有者认为这个特性是个坏主意。
注意:作者的"in app"系统看起来像是使用字符串键进行查找,但每次仍然需要不同的类(DefaultRequestHandler
, OrdersRequestHandler
和CustomersRequestHandler
)。我只是有一个GenericRepostory
,它允许我抽象我的存储库方法,不管我连接到什么。
我想我可以继承我的GenericRepostory
每次我想实例化它。或者让它取一个我不需要的随机类型参数。但这打乱了我的设计,所以我希望有另一种方法。
那么是否有任何工作,不让我创建伪造类型来区分我的两个IRepository/GenericRepository实例?
我们最终改变了我们的通用存储库,看起来像这样:
/// The Type parameter has no funcionality within the repository,
/// it is only there to help us differentiate when registering
/// and resolving different repositories with Simple Injector.
public class GenericRepository<TDummyTypeForSimpleInjector> : IRepository
(我们给它添加了一个类型参数)
然后,我们像这样创建了两个虚拟类(我更改了类的名称以匹配我的示例):
// These are just dummy classes that are used to help
// register and resolve GenericRepositories with Simple Injector.
public class FirstDataSelector { }
public class SecondDataSelector { }
然后我可以像这样注册它们:
container.Register<FirstData>(() => new FirstData());
container.Register(() => new GenericRepository<FirstDataSelector>
(container.GetInstance<FirstData>()));
container.Register<SecondEntities>(() => new SecondEntities());
container.Register(() => new GenericRepository<SecondDataSelector>
(container.GetInstance<SecondData>()));
(注意GenericRepository上的泛型类型参数,我没有将其注册为IRepository。
这很好。然后我就可以在业务逻辑的构造函数注入中使用该注册。
container.Register<IFirstBusiness>(() => new FirstBusiness
(container.GetInstance<GenericRepository<FirstDataSelector>>()));
container.Register<ISecondBusiness>(() => new SecondBusiness
(container.GetInstance<GenericRepository<SecondDataSelector>>()));
由于我的业务类采用IRepository
,因此它工作得很好,并且不会向业务类公开IOC容器或存储库的实现。
我基本上使用Type参数作为查找的键。(我知道,但我的选择有限。)
不得不在我的设计中添加虚拟类有点令人失望,但我们的团队决定,缺点是值得的,而不是放弃简单注入器并回到Unity。
你自己的答案实际上很好,但不幸的是,你把泛型类型参数看作是一个虚拟的;你应该让它成为你设计的头等大事:
public interface IRepository<TData> { }
public clss GenericRepository<TData> : IRepository<TData>
{
public GenericRepository(TData data) { }
}
这样你就可以简单地注册它们如下:
container.Register<IRepository<FirstData>, GenericRepository<FirstData>>();
container.Register<IRepository<SecondData>, GenericRepository<SecondData>>();
在这种情况下,您的业务类可以简单地依赖于通用的IRepository<FirstData>
和IRepository<SecondData>
,并且可以简单地按照以下方式注册:
container.Register<IFirstBusiness, FirstBusiness>();
container.Register<ISecondBusiness, SecondBusiness>();
注意这里给出的注册没有使用任何lambda。简单的注射器可以找到这个为您。这使得你的DI配置更简单,更易读,尤其是:更易维护。
这样你可以使你的设计非常明确和明确。您的设计是模糊的,因为您有一个单一的(非通用的)IRepository
接口,应该映射到几个实现。虽然这并不是在所有情况下都很糟糕,但在大多数情况下,这种模糊性可以而且应该被防止,因为这会使您的代码和配置复杂化。
此外,由于您的通用GenericRepository<T>
现在映射到通用IRepository<T>
,我们可以用一行替换所有Register<IRepository<T>, GenericRepository<T>>()
注册:
// using SimpleInjector.Extensions;
container.RegisterOpenGeneric(typeof(IRepository<>),
typeof(GenericRepository<>);
更进一步,您的业务类也可能受益于泛型类型。以本文为例,每个业务操作都有自己的类,但所有业务操作都隐藏在相同的通用ICommandHandler<TCommand>
抽象之后。当您这样做时,所有业务类都可以通过单个调用注册:
container.RegisterManyForOpenGeneric(typeof(ICommandHandler<>),
typeof(ICommandHandler<>).Assembly);
这个调用在提供的程序集中搜索ICommandHandler<TCommand>
接口的实现,并在容器中注册每个找到的实现。您可以添加新的业务逻辑片段(用例),而不必更改配置。但这只是对业务逻辑进行这种抽象的众多优势之一。另一个巨大的优点是,它使添加横切关注点(如日志记录、事务处理、安全性、审计跟踪、缓存,您可以想到的)更容易实现。