我有一个应用程序,我的DbContext
实现有多个连接字符串。为了在消费类中支持这一点,我创建了一个IDbContextProvider
接口,其中Get
方法可以为我提供我需要的DbContext
个实例。
我还有一个ICommandHandler
的东西,我正在尝试创建一个装饰器,它会在成功执行命令时调用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>
而不是其中一个注册的我的应用。
SaveChangesCommandHandlerDecorator类型的构造函数&lt; CreateUserCommand&gt;包含名称为“dbContextProvider”的参数,并键入IDbContextProvider&lt; DbContext&gt;那是没有注册的。请确保IDbContextProvider&lt; DbContext&gt;已注册,或更改SaveChangesCommandHandlerDecorator&lt; CreateUserCommand&gt;的构造函数。请注意,存在不同类型的注册(编辑).IDbContextProvider&lt; TDbContext&gt;请求的类型是(编辑).IDbContextProvider&lt; Microsoft.EntityFrameworkCore.DbContext&gt;。
这实际上是有道理的,因为Simple Injector无法知道注入dbContextProvider
参数的具体类型。
有没有办法自定义我的装饰器的创建方式,以便它可以查看底层ICommandHandler
实现的依赖关系的依赖关系,并在创建时从那里选择IDbContextProvider
签名?因此,如果我的命令处理程序有一个IDbContextProvider<AwesomeDbContext>
,我也希望为我的装饰器解析它。
答案 0 :(得分:2)
让我直截了当地说明:您的应用程序包含多个DbContext
实现,例如:
现在,每个命令处理程序通常依赖于一个特定的DbContext
实现,方法是获取IDbContextProvider<TDbContext>
TDbContext
是特定DbContext
实现的位置。
现在取决于装饰器命令处理程序所依赖的内容,您希望在装饰器中注入完全相同的IDbContextProvider<TDbContext>
实现,这样您就可以保存该特定实例的更改。
如果我正确地总结了你的问题,你问题的简短回答是:不,你不能这样做。
答案越长,是的,实际上可以通过向TDbContext
装饰器添加SaveChangesCommandHandlerDecorator<TCommand, TDbContext>
的额外泛型类型参数来实现,并使用接受{{1}的RegisterDecorator
重载}}。使用提供的Func<DecoratorPredicateContext, Type>
工厂委托,您可以分析其DecoratorPredicateContext
属性以找出注入的Expression
,并根据该信息构建部分关闭的{{}}版本1}}您在其中填写IDbContextProvider<TDbContext>
,但保留SaveChangesCommandHandlerDecorator<TCommand, TDbContext>
以便Simple Injector填写。
但说实话,我不会采取这条路线。这需要大量的工作,导致难以理解的代码,而你的同事会因此而讨厌你。
相反,请尝试将TDbContext
个实例列表注入装饰器中。您可以单独进行TCommand
注册,也可以使用IDbContextProvider<TDbContext>
作为集合的一部分来完成此操作。
当您注入集合时,装饰器可以简单地在所有IDbContextProvider<out TDbContext>
实例上调用保存更改。如果没有变化,RegisterCollection
会非常快,所以从表现的角度来看,我认为没有什么可担心的。