【问题标题】:Simple Injector: Inject External Web Service简单注入器:注入外部 Web 服务
【发布时间】:2014-05-06 13:20:24
【问题描述】:

我在如何使用 SimpleInjector 正确配置 DI 时遇到了一些困难。我有一个外部 Web 服务,其绑定位于 Web.config 文件中。我的外部 Web 服务位于服务层中。我的 Web 层包含组合根,它调用我的域层来注册服务 - 域层然后将调用 DAL 层和服务层来注册它需要的服务。这可以正常工作,然后在我的域层中,我可以使用我在域服务层构造函数中的服务层上创建的注入服务。

但是在我的服务层中,我有类似下面的内容:

public class MyService : IMyService
{

    private readonly ExternalServiceClient _externalServiceClient;

    public MyService()
    {
        _externalServiceClient = new ExternalServiceClient("WSHttpBinding_IExternalService");
    }

这种设计可能不是最好的,因为它将 MyService 紧密耦合到依赖外部 ServiceClient - 我想要实现的是能够拥有我自己的外部客户端存根,然后在实际的外部服务客户端或我的外部服务客户端的存根版本。

所以我的构造函数看起来像:

    private readonly ExternalServiceClient _externalServiceClient;

    public MyService(ExternalServiceClient externalServiceClient)
    {
        _externalServiceClient = externalServiceClient);
    }

其中 - externalServiceClient 是新的

ExternalServiceClient("WSHttpBinding_IExternalService"); 或我的外部客户端的存根版本。

我不确定 SimpleInjector 的问题是如何正确连接它,以便我可以轻松地在哪个 ExternalClient 传递到构造函数之间切换?

【问题讨论】:

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


    【解决方案1】:

    所以IMyService 是你自己的抽象,ExternalServiceClient 是一些生成的代理,MyService 只是委托给ExternalServiceClient?如果是这样的话,我会说MyService 实现与ExternalServiceClient 强耦合是很好的。它不包含任何逻辑,只是一些允许将ExternalServiceClient 隐藏在抽象后面的基础设施。您可以简单地创建一个存根IMyService,而不是创建一个存根ExternalServiceClient

    此外,如果ExternalServiceClient 是WCF 生成的代理,您可能希望让MyService 类控制ExternalServiceClient 的创建和处置,特别是因为WCF services need special care with disposing。如果您让您的容器控制ExternalServiceClient 的处置,那么无论您选择什么容器,您都会使您的 DI 配置更加困难。

    无论哪种方式,如果您决定将创建和处置移到 MyService 之外,最简单的注册方法如下:

    container.Register<IMyService>(() => new MyService(
        new ExternalServiceClient("WSHttpBinding_IExternalService")));
    

    然而,这个注册的问题是ExternalServiceClient 没有被释放。如果您在具有 Scoped 生活方式之一的容器中显式注册 ExternalServiceClient,则容器将自动为您处理 dispose:

    container.Register<IMyService, MyService>();
    
    container.RegisterPerWebRequest<ExternalServiceClient>(
        () => new ExternalServiceClient("WSHttpBinding_IExternalService"));
    

    但是,范围生活方式意味着 ExternalServiceClient 在整个请求中被重用,这可能不是您所需要的。因此,或者,您可以将其注册为瞬态,并允许在请求结束时释放任何已创建的实例:

    var scopedLifestyle = new WebRequestLifestyle();
    
    container.Register<IMyService, MyService>();
    
    container.Register<ExternalServiceClient>(
        () => new ExternalServiceClient("WSHttpBinding_IExternalService"));
    
    container.RegisterInitializer<ExternalServiceClient>(client =>
    {
        scopedLifestyle.RegisterForDisposal(container, client);
    });
    

    这个配置的问题当然是 WCF 代理是讨厌的 fâckers,可能会从它们的 Dispose 方法中抛出异常,所以你需要做一些额外的处理才能做到这一点:

    var scopedLifestyle = new WebRequestLifestyle();
    
    container.Register<IMyService, MyService>();
    
    container.Register<ExternalServiceClient>(
        () => new ExternalServiceClient("WSHttpBinding_IExternalService"));
    
    container.RegisterInitializer<ExternalServiceClient>(client =>
    {
        scopedLifestyle.WhenScopeEnds(container, () =>
        {
            try
            {
                client.Dispose();
            }
            catch 
            {
                // According to Marc Gravell we need to have a catch all here. 
            }
        });
    });
    

    这将有效地解决问题。

    【讨论】:

    • (comment part 1 of 2) 只是看到你的答案 - 我实际上已经实现了你所说的最简单的方法是...... - 但是我没有考虑过有效的处置所以我会对此做一些重新工作。是的,您是对的 - ExternalService 客户端是第 3 方服务,然后我的服务是我的抽象调用,仅调用我需要的方法以及一些额外的逻辑。所以也许它就像我最初的那样没问题。
    • comment part 2 of 2) - 我想要 DI 的原因是,如果团队中的其他开发人员没有在服务所需的机器上安装 x509 证书,他们可以轻松注释掉 Simple 容器.Register ExternalServiceClient("WSHttpBinding_IExternalService") 并在容器中注释。Register new MyStubExternalClient
    • @Steven,我正在将 WCF 服务用于我的命令和查询。我喜欢依赖处理程序上的客户端(或客户端)。您是否建议我像您的示例一样设置客户端并注册初始化程序?
    • @janhartmann 使应用程序特定抽象(遵循 SOLID)并创建一个适配器来创建/处置 wcf 代理,如我的示例所示。
    • 我会看看我能想出什么,如果我失败了,你可能会在 SO 上找到一个问题。谢谢@Steven :-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多