我有一个应用程序,其中有多个用于DbContext
实现的连接字符串。为了在消费类中支持这一点,我创建了一个带有Get
方法的IDbContextProvider
接口,该接口可以为我提供所需的DbContext
实例。
我还有一个ICommandHandler
,我正在尝试创建一个decorator,它将在成功执行命令时调用DbContext.SaveChangesAsync()
。我正在注册我的装饰师,如下所示:
container.RegisterDecorator(typeof(ICommandHandler<>),
typeof(SaveChangesCommandHandlerDecorator<>));
现在,因为我不想为DbContext
实现添加类型参数,而且我知道从DbContext
派生的所有类都有SaveChangesAsync()
方法,所以我想我可以使用协方差。所以我的界面是这样的:
public public interface IDbContextProvider<out TDbContext> where TDbContext : DbContext
{
TDbContext Get(DbPrivileges privileges);
}
我的装饰师的相关部分:
public class SaveChangesCommandHandlerDecorator<TCommand> : ICommandHandler<TCommand>
where TCommand : ICommand
{
private readonly ICommandHandler<TCommand> _handler;
private readonly IDbContextProvider<DbContext> _dbContextProvider;
public SaveChangesCommandHandlerDecorator(
ICommandHandler<TCommand> handler, IDbContextProvider<DbContext> dbContextProvider)
{
_handler = handler;
_dbContextProvider = dbContextProvider;
}
...
然而,当我在容器上调用Verify()
时,它会抱怨IDbContextProvider
无效,因为它正在寻找"基本"IDbContextProvider<DbContext>
,而不是在我的应用程序中注册的CCD_12。
SaveChangesCommandHandlerRecorder<类型的构造函数;CreateUserCommand>包含名为"dbContextProvider"、类型为IDbContextProvider<未注册的DbContext>。请确保IDbContentProvider<DbContext>已注册,或者更改SaveChangesCommandHandlerRecordor<CreateUserCommand>。请注意,存在一个不同类型的注册(Redacted)。IDb内容提供程序<TDbContext>,而请求的类型为(已修改)。IDb内容提供程序<微软EntityFrameworkCore。DbContext>。
这实际上是有道理的,因为Simple Injector无法知道要向dbContextProvider
参数中注入什么具体类型。
是否有任何方法可以自定义我的decorator的创建方式,以便它可以查看底层ICommandHandler
实现的依赖项的依赖项,并在创建时从中选择IDbContextProvider
签名?因此,如果我的命令处理程序有一个IDbContextProvider<AwesomeDbContext>
,我希望它也能为我的decorator解决。
让我直白地说:您的应用程序包含多个DbContext
实现,例如:
- 真棒DbContext
- EventBetterDbContext
- BrilliantDbContext
现在,每个命令处理程序通常将依赖于一个特定的DbContext
实现,通过获得IDbContextProvider<TDbContext>
,其中TDbContext
是特定的CCD21实现。
现在,根据decorator命令处理程序所依赖的内容,您希望在decorator中也注入完全相同的IDbContextProvider<TDbContext>
实现,这样您就可以保存该特定实例的更改。
如果我正确地总结了你的问题,你的问题的简短答案是:不,你不能那样做。
较长的答案是,是的,这实际上是可能的,方法是向SaveChangesCommandHandlerDecorator<TCommand, TDbContext>
装饰器添加一个额外的TDbContext
泛型类型参数,并使用接受Func<DecoratorPredicateContext, Type>
的RegisterDecorator
重载。使用向工厂委托提供的DecoratorPredicateContext
,您可以分析其Expression
属性,以找出注入的IDbContextProvider<TDbContext>
,并基于该信息构建SaveChangesCommandHandlerDecorator<TCommand, TDbContext>
的部分封闭版本,在该版本中,您填写TDbContext
,但保持TCommand
打开,以便Simple Injector填写。
但说实话,我不会走这条路。这需要大量的工作,导致代码难以理解,你的同事会因此讨厌你。
相反,请尝试将IDbContextProvider<TDbContext>
实例的列表注入decorator。您可以通过单独注册IDbContextProvider<out TDbContext>
,也可以使用RegisterCollection
作为集合的一部分进行注册。
注入集合时,装饰器可以简单地调用所有DbContext
实例上的保存更改。SaveChanges
在没有变化的情况下会非常快,所以从性能的角度来看,我认为没有什么可担心的。