【问题标题】:WCF and dependency injectionWCF 和依赖注入
【发布时间】:2012-02-09 21:26:57
【问题描述】:

为了测试目的,我有一组使用依赖注入实现的 WCF 服务。

基本上这些服务有时会相互调用,因此在我的单元测试中,我可以使用接口模拟存储库和 WCF 服务,并将它们作为组件注入服务构造函数中。

这很棒,因为我可以在没有任何依赖的情况下正确测试这些服务。

我还实现了自己的代理,以避免使用 Visual Studio 服务引用(这会产生很多垃圾),所以我使用的是 ChannelFactory CreateChannel 方法。

我有点担心现场环境会发生什么。

发生的情况是,调用另一个 WCF 服务的 WCF 服务只有一个注入到构造函数中的外部组件实例,因此我无法在使用后处理此对象。

这会造成麻烦吗? 垃圾收集器会处理它吗? 连接会保持打开状态吗? 这种做法有错吗?

非常感谢。

【问题讨论】:

    标签: .net wcf unit-testing dependency-injection


    【解决方案1】:

    是的,您应该关闭您打开的频道。您还没有指定谁实际使用通道工厂打开通道。任何人从调用工厂获得开放通道的人都有责任关闭它,因为这是唯一知道何时允许这样做的地方。

    一般情况下,您应该打开您的频道,拨打电话,然后立即关闭它以释放与池的连接。 ChannelFactory<T> 对此进行了优化,将在可行时重用可用连接,并且只处理合同等一次。

    鉴于这些要点,到目前为止,我最喜欢的是使用另一个抽象来明确定义这些打开-调用-关闭语义。我称之为IChannelInvoker<T>,它有一个Execute 方法。我现在拥有的版本只是异步的,但为了简单起见,这里是同步版本的样子:

    public interface IChannelInvoker<TChannel>
    {
        /// <summary>
        /// Executes a method within the context of an open channel
        /// </summary>
        TResult Execute<TResult>(Func<TChannel, TResult> method);
    }
    

    实现的Execute 方法使用ChannelFactory 打开通道、调用委托、关闭通道并返回结果。我在那里注入了工厂,并在应用程序的整个生命周期内一直存在。

    现在在服务 A 中,不是注入通道 T,而是注入 IChannelInvoker&lt;T&gt;。例如:

    public class ServiceA : IServiceA
    {
        private readonly IChannelInvoker<IServiceB> _b;
    
        public ServiceA(IChannelInvoker<IServiceB> b)
        {
            _b = b;
        }
    
        public void SomeOperationA()
        {
            _b.Execute(channel => channel.SomeOperationB());
        }
    }
    

    调用程序也是可组合的,因此您可以添加其他 IChannelInvoker&lt;T&gt; 装饰器来应用横切关注点,例如消息级别的安全性 - 添加标头等。

    【讨论】:

      【解决方案2】:

      当您调用 CreateChannel 方法时,ChannelFactory 会创建一个通道对象。这个通道对象是真正有连接的对象。

      一旦你完成了通道,你应该通过调用它的 IClientChannel.Close 方法来关闭它。这应该会导致任何底层网络连接关闭。请注意,在某些情况下,调用 Close 方法可能会导致 CommunicationObjectFaultedException 异常,即使您首先验证您的频道未处于 Faulted 状态。

      您可以在code example of this MSDN documentation page 中看到,在 wcfClientChannel 对象上调用了 Close 方法。

      【讨论】:

      • 感谢您的回复,但我的问题不是如何关闭连接。我想知道如何将 WCF/Web 服务作为组件注入并正确处理服务连接。问题在于,将它作为构造函数中的参数意味着在类级别我只有一个实例,如果调用另一种方法,则一旦关闭,就会引发异常。这也是事实,默认情况下每个服务调用都是一个不同的实例,所以这可能只是一个哲学问题。希望它有意义。
      • @matteo75 我想我不清楚您的方法“外部组件”是通道工厂还是通道。如果是渠道厂,那我就不太关心处置了。我认为你的方法很好,但我会让你的服务实现实现 IDisposable 并处理你的外部组件。在那里设置一个中断只是为了验证 WCF 尊重它,它应该这样做。
      • 我正在使用 ChannelFactory 但我目前还没有实现任何服务 Disposal。我认为这很好,但我注意到 IIS 上的内存使用量增加(在回收后释放),这可能是由于这种未释放服务连接。我不是 100% 肯定,这只是一个猜测,但如果了解依赖注入架构中服务关闭/处置的最佳实践,那就太好了。