The solution I eventually came up with was to assign a unique identifier to every message that was published, and cache the published message in the service adapter (in the same place where I was storing the callbacks to the subscribed clients. Whenever I published the message, subscribers would receive the message and the corresponding unique id. Subscribers could then use the channel.Faulted event to reconnect and resubscribe to the service with a special method that takes the last received message id as a parameter.
Service code:
/// <summary>
/// Operation used by the subscriber to subscribe to events published.
/// </summary>
public void Resubscribe(int lastReceivedMessageId)
{
// Get callback contract
IPubSubCallback callback = OperationContext.Current.GetCallbackChannel<IPubSubCallback>();
ThreadPool.QueueUserWorkItem(delegate(object state)
{
adapter.Resubscribe(lastReceivedMessageId, callback);
});
}
Adapter code:
/// <summary>
/// Operation used by the subscriber to resubscribe to events published.
/// </summary>
public void Resubscribe(int lastReceivedMessageId, IPubSubCallback callback)
{
try
{
// Send the subscriber any missed messages
foreach (KeyValuePair<int, string> missedMessage in publishedMessages.Where(x => x.Key > lastReceivedMessageId))
{
callback.MessagePublished(missedMessage.Value, missedMessage.Key);
}
// Add the subscriber callback to the list of active subscribers
if (!callbacks.Contains(callback))
{
callbacks.Add(callback);
}
}
catch
{
// ignore subscription, callbacks failed again
}
}
The service can then work out what the client has missed, and resend those messages in the correct order.
This solution seems to be working well for me, but I have a feeling that there must be a better way to do this. Comments / additional answers are very welcome! :)