【问题标题】:How to Make Alternative Use of Interfaces for Wcf Contracts如何替代使用 Wcf 合同的接口
【发布时间】:2011-08-16 21:42:47
【问题描述】:

假设我有 3 个程序集,Example.Core、Example.Contracts、Example.WcfServices。在我的合同程序集中,我定义了一个接口并添加了一些操作,例如ICalculator,具有操作 Add(double a, double b)。在我的 WcfServices 程序集中,我有一个作为 Wcf 服务公开的 ICalculator 实现。

现在我的问题是……在我的 Example.Core 程序集中,我如何针对该接口进行编程,同时保持一切解耦(以允许我有该接口的替代实现)。如果我有一个需要 ICalculator 的类,我可以从 ChannelFactory 创建一个并使用它,或者我可以在构造函数中注入一个实例。如果我在类中创建一个,那么我将依赖项放在 ChannelFactory/Wcf 上的类中,我真的不想这样做。如果我在构造函数中注入一个实例,那么注入类将如何管理和整理 wcf 服务?似乎虽然我有一个界面,但我没有干净的方式使用它。我看过类似 NInject 的东西,但我不相信它会在 ChannelFactory 出现故障时清理它(至少我还没有找到任何文档表明它知道何时在通道上调用 Abort 而不是 Close)。

我最终做的是再次实现我的界面并使用此问题中描述的方法:creating WCF ChannelFactory<T> 并仅调用服务上的方法。这对我来说有点“味道”,因为我再次打包所有电话只是为了确保频道正确关闭/中止。

是否有任何模式/方法干净地具有两个接口的实现,其中之一是 Wcf 服务?

谢谢,

迈克。

【问题讨论】:

  • 你问的是服务端还是客户端?
  • 我想知道客户端。澄清一下,如果我在单独的程序集中创建一个接口并且有多个实现,由于难以管理 wcf 实现的对象生命周期,交换实现变得困难,那么最好的模式是什么?
  • 好的,谢谢。我想我明白了......我需要注入一个工厂而不是接口,并且工厂负责管理代理的生命周期(并且在 Channel 的情况下,它可能应该挂钩其故障事件以正确关闭它如果它有故障)。谢谢。

标签: wcf inversion-of-control ninject


【解决方案1】:

我使用了答案linked in Mark's commentthis article here 的变体来提出对我有用的解决方案。

鉴于您定义的服务合同,第 1 步将定义一个实现您的服务的接口和IClientChannel

// Service Contract
public interface ICalculator
{
    Add(double a, double b);
}

// Interface to expose Close and Abort
public interface ICalculatorChannel : ICalculator, IClientChannel { }

第 2 步涉及创建可重用代码,该代码将处理创建代理并实现代码以关闭或中止连接。

public class ServiceClient<T> where T : class, IClientChannel
{
    private ProxyGenerator _generator = new ProxyGenerator();

    public T CreateProxy(string endpointConfigurationName)
    {
        return _generator.CreateInterfaceProxyWithoutTarget<T>
            (new WcfInterceptor<T>(endpointConfigurationName));
    }
}

ServiceClient&lt;T&gt; 中的 T 将采用步骤 1 中定义的 ICalculatorChannel。ProxyGenerator 是 Castle 项目的一部分。它在这里的用途是允许我们拦截对 WCF 服务的调用并执行 pre 和 post 操作。

public class WcfInterceptor<T> : IInterceptor where T : IClientChannel
{
    private ChannelFactory<T> _factory = null;

    public WcfInterceptor(string endpointConfigurationName)
    {
        _factory = new ChannelFactory<T>(endpointConfigurationName);
    }

    public void Intercept(IInvocation invocation)
    {
        T channel = _factory.CreateChannel();

        try
        {
            invocation.ReturnValue = invocation.Method.Invoke
                (channel, invocation.Arguments);
        }
        finally
        {
            closeChannel(channel);
        }
    }
}

如您所见,Intercept 方法封装了通道和关闭通道的调用。 closeChannel 方法将处理调用Close()Abort() 的决定。

    private void closeChannel(T channel)
    {
        if (channel != null)
        {
            try
            {
                if (channel.State != CommunicationState.Faulted)
                {
                    channel.Close();
                }
                else
                {
                    channel.Abort();
                }
            }
            catch
            {
                channel.Abort();
            }
        }
    }

现在我们创建一个类来封装这个 ServiceClient 的使用。

public class Calculator : ICalculator
{
    private var _calcualtor = new ServiceClient<ICalculatorChannel>();

    public void Add(double a, double b)
    {
        var proxy = _calculator.CreateProxy("endpointConfigGoesHere");

        return proxy.Add(a, b);
    }
}

请注意,添加的实现不再需要处理 WCF 连接问题。消费类只需要知道服务契约。 ServiceClient 类现在为我们处理了处理错误连接的丑陋。

客户端现在只需要承担一个依赖。

public class MyClient
{
    private ICalculator _calculator;

    public MyClient(ICalculator calculator)
    {
        _calculator = calculator;
    }
}

并且 IOC 可以填充客户端:

Bind<ICalculator>().To<Calculator>();

【讨论】:

  • 谢谢..你所描述的几乎正是我最终选择的路线。我使用了这个例子中管理的通道工厂,它的工作方式和你上面描述的一样。 stackoverflow.com/questions/3200197/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-07
  • 1970-01-01
  • 1970-01-01
  • 2011-08-08
相关资源
最近更新 更多