简单的注入器装饰器和协方差



我有一个应用程序,其中有多个用于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在没有变化的情况下会非常快,所以从性能的角度来看,我认为没有什么可担心的。

相关内容

  • 没有找到相关文章

最新更新