如何为Wcf合同替代使用接口

Mik*_*han 2 wcf ninject inversion-of-control

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

现在我的问题是这个......在我的Example.Core程序集中,如何在保持所有内容解耦的同时对该接口进行编程(以允许我有一个替代的接口实现).如果我有一个需要ICalculator的类,我可以创建一个来自ChannelFactory并使用它,或者我可以在构造函数中注入一个实例.如果我在课堂上创建一个,那么我在ChannelFactory/Wcf中将依赖项放在我的班级中,我真的不想这样做.如果我在构造函数中注入一个实例,那么注入类将如何管理和整理wcf服务?似乎虽然我有一个界面,但我没有干净的使用方法.我已经看过像NInject这样的东西了,但我不相信如果它出现故障会清理ChannelFactory(至少我没有发现任何文档显示它知道何时调用Abort而不是在通道上关闭).

我最终做的是再次实现我的界面并使用此问题中描述的方法:创建WCF ChannelFactory <T>并只调用服务上的方法.这对我来说有点"闻起来",因为我再次包裹所有的电话只是为了确保通道正确关闭/中止.

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

谢谢,

麦克风.

Phi*_*son 5

在Mark的评论本文中使用了一个答案的变体来提出一个对我有用的解决方案.

根据您定义的服务合同,第1步将定义实现您的服务的接口IClientChannel.

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

// Interface to expose Close and Abort
public interface ICalculatorChannel : ICalculator, IClientChannel { }
Run Code Online (Sandbox Code Playgroud)

第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));
    }
}
Run Code Online (Sandbox Code Playgroud)

T in ServiceClient<T> 将采用ICalculatorChannel步骤1中定义的内容.ProxyGenerator是Castle项目的一部分.它的用法是允许我们拦截对WCF服务的调用并执行前后操作.

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

如您所见,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();
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

现在我们创建一个类来包装此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);
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,add的实现不再需要处理WCF连接问题.消费类只需了解服务合同即可.处理故障连接的丑陋现在由ServiceClient班级为我们照顾.

客户端现在只需要一个依赖项.

public class MyClient
{
    private ICalculator _calculator;

    public MyClient(ICalculator calculator)
    {
        _calculator = calculator;
    }
}
Run Code Online (Sandbox Code Playgroud)

IOC可以填充客户端:

Bind<ICalculator>().To<Calculator>();
Run Code Online (Sandbox Code Playgroud)