【问题标题】:Simple Injector decorator and covariance简单的注入器装饰器和协方差
【发布时间】:2017-11-13 21:32:19
【问题描述】:

我有一个应用程序,其中我的DbContext 实现有多个连接字符串。为了在消费类中支持这一点,我创建了一个带有Get 方法的IDbContextProvider 接口,它可以为我提供我需要的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&lt;DbContext&gt; 而不是在我的应用中注册的其中之一。

SaveChangesCommandHandlerDecorator 类型的构造函数包含名称为“dbContextProvider”且类型为 IDbContextProvider 的未注册参数。请确保 IDbContextProvider 已注册,或更改 SaveChangesCommandHandlerDecorator 的构造函数。请注意,存在不同类型 (Redacted).IDbContextProvider 的注册,而请求的类型是 (Redacted).IDbContextProvider

这实际上是有道理的,因为 Simple Injector 无法知道将什么具体类型注入到 dbContextProvider 参数中。

是否有任何方法可以自定义我的装饰器的创建方式,以便它可以查看底层 ICommandHandler 实现的依赖项的依赖项,并在创建时从那里选择 IDbContextProvider 签名?所以如果我的命令处理程序有一个IDbContextProvider&lt;AwesomeDbContext&gt;,我希望我的装饰器也能解决这个问题。

【问题讨论】:

    标签: c# dependency-injection ioc-container simple-injector


    【解决方案1】:

    所以让我直截了当地说:您的应用程序包含多个 DbContext 实现,例如:

    • AwesomeDbContext
    • EventBetterDbContext
    • BrilliantDbContext

    现在每个命令处理程序通常将依赖于一个特定的DbContext 实现,方法是获取一个IDbContextProvider&lt;TDbContext&gt;,其中TDbContext 是特定的DbContext 实现。

    现在,根据装饰器命令处理程序所依赖的内容,您也希望在装饰器中注入完全相同的 IDbContextProvider&lt;TDbContext&gt; 实现,以便保存该特定实例的更改。

    如果我正确地总结了你的问题,你的问题的简短回答是:不,你不能那样做。

    更长的答案是,是的,这实际上可以通过为TDbContext 添加一个额外的泛型类型参数到您的SaveChangesCommandHandlerDecorator&lt;TCommand, TDbContext&gt; 装饰器,并使用接受Func&lt;DecoratorPredicateContext, Type&gt;RegisterDecorator 重载。使用提供给工厂委托的DecoratorPredicateContext,您可以分析其Expression 属性以找出注入了哪个IDbContextProvider&lt;TDbContext&gt;,并根据该信息构建SaveChangesCommandHandlerDecorator&lt;TCommand, TDbContext&gt; 的部分封闭版本,您可以在其中填写@ 987654335@,但将TCommand 留空以供 Simple Injector 填写。

    但老实说,我不会走这条路。这需要大量工作,导致代码难以理解,而且你的同事会因此而讨厌你。

    改为尝试将IDbContextProvider&lt;TDbContext&gt; 实例列表注入装饰器。您可以通过单独注册IDbContextProvider&lt;out TDbContext&gt; 来做到这一点,也可以使用RegisterCollection 作为集合的一部分。

    当您注入集合时,装饰器可以简单地调用所有DbContext 实例的保存更改。 SaveChanges 在没有变化的情况下会非常快,所以从性能的角度来看,我认为没有什么可担心的。

    【讨论】:

    • 是的,这正确地总结了我的问题,感谢您的详细回答!我没有考虑收集路线,但它肯定会产生更清晰的代码,所以我会这样做。
    猜你喜欢
    • 2012-11-06
    • 1970-01-01
    • 1970-01-01
    • 2015-09-05
    • 2012-06-17
    • 2017-02-05
    • 1970-01-01
    • 2020-02-23
    • 1970-01-01
    相关资源
    最近更新 更多