CallbackContract是一对一沟通的好策略吗?

时间:2016-11-30 15:29:35

标签: c# wcf

背景

我有一对一关系的WCF客户端和服务(即每个客户端一个服务主机)。我使用回调契约将消息从服​​务传递到客户端。

[ServiceContract(CallbackContract = typeof(IMessageTarget)]
public interface IMessageService
{
    [OperationContract]
    void StartMessages();
}

public interface IMessageTarget
{
    [OperationContract]
    Task SendAsync(Message message);

    [OperationContract(IsOneWay = true)]
    void Complete();

    [OperationContract(IsOneWay = true)]
    void Fault(ExceptionDetail exception);
}

在客户端上,我设置了双工连接以开始通信。调用MessageTarget.CompletionComplete()方法时Fault(ExceptionDetail)完成。

IMessageTarget target = new MessageTarget();
var channelFactory = new DuplexChannelFactory<IMessageService>(target, new NetNamedPipeBinding());
IMessageService service = channelFactory.CreateChannel(new EndpointAddress(EndpointUri));

service.StartMessages();
await target.Completion;

服务一旦可用,就会向客户端发送消息。客户端可以通过让SendAsync()花费很长时间返回来延迟更多消息。

IMessageTarget client = OperationContext.Current.GetCallbackChannel<IMessageTarget>();

// later, whenever a message is created
await client.SendAsync(message);

// finally
client.Complete();

问题

我发现如果服务崩溃,客户端会挂起。不幸的是,除了调用服务方法并捕获CommunicationException之外,WCF没有提供检测服务是否仍然运行的可靠方法。因此,我添加了一个新的操作

[OperationContract]
Task PingAsync();

因此客户端可以监控服务是否停止。

service.StartMessages();
while (!target.Completion.IsCompleted)
{
    await service.PingAsync();
    await Task.Delay(TimeSpan.FromSeconds(0.5));
}
await target.Completion;

问题

这不会破坏使用回调的目的吗?我一直在努力想看看这个&#34;在不断砰砰的同时聆听回调&#34;策略优于简单地轮询最新消息(不需要双工)。我是否过度设计了这个,或者回调还有其他好处吗?

1 个答案:

答案 0 :(得分:1)

大多数与WCF相关的异常都来自CommunicationsException,所以你在这个意义上做得很好。正如您所指出的,ping / polling违背了回调实现的目的。最重要的是,您需要调用服务并准备好处理服务调用期间发生的任何异常。没有可靠的方法来确定该服务是否可用。

如果您还没有这样做,您还可以查看IClientChannel事件,以监控连接发生的情况。

client.InnerChannel.Closed += OnChannelClosed;
client.InnerChannel.Opening += OnChannelOpening;
client.InnerChannel.Opened += OnChannelOpened;
client.InnerChannel.Closing += OnChannelClosing;
client.InnerChannel.Faulted += OnChannelFaulted;
client.InnerChannel.UnknownMessageReceived += OnChannelUnknownMessageReceived;