I have application that notifies about new mail, created using EWS managed api (v2.2). I am using StreamingSubscription for listening mailbox events. I've noticed that sometimes NewMail event not fired (I see unhandled mail in mailbox later), no connection/subscription exception being raised, and after some period of time new mail receiving as nothing happened... what could cause such behavior? There's nothing suspicious in exchange logs, event just not being fired... Any Ideas and suggestion would be appreciated.
Thanks.
Source:
public class ExchangeClient : IDisposable
{
private ExchangeService _exchange;
private SubscriptionBase _subscription;
private StreamingSubscriptionConnection _connection;
private bool _disposed;
private bool _disposing;
public event EventHandler<ExchangeEventArgs> ExchangeEvent;
public ExchangeClient(string userName, string password, string domain, ExchangeVersion version)
{
_exchange = new ExchangeService(version);
_exchange.Credentials = new WebCredentials(userName, password);
_exchange.AutodiscoverUrl(userName + "@" + domain);
var ids = new FolderId[2] { new FolderId(WellKnownFolderName.Root), new FolderId(WellKnownFolderName.Inbox) };
var events = new List<EventType>();
events.Add(EventType.NewMail);
_subscription = _exchange.SubscribeToStreamingNotifications(ids, events.ToArray());
_connection = new StreamingSubscriptionConnection(_exchange, 30);
_connection.AddSubscription((StreamingSubscription)_subscription);
_connection.OnNotificationEvent += OnNotificationEvent;
_connection.OnSubscriptionError += OnSubscriptionError;
_connection.OnDisconnect += OnDisconnect;
_connection.Open();
}
private void OnDisconnect(object sender, SubscriptionErrorEventArgs args)
{
if (!_disposing && _connection != null)
{
_connection.Open();
}
}
private void OnSubscriptionError(object sender, SubscriptionErrorEventArgs args)
{
throw args.Exception;
}
private void OnNotificationEvent(object sender, NotificationEventArgs args)
{
if (_subscription != null)
{
if (args.Subscription.Id == _subscription.Id)
{
foreach (var notificationEvent in args.Events)
{
switch (notificationEvent.EventType)
{
case EventType.Status:
break;
case EventType.NewMail:
NotificationReceived(new ExchangeEventArgs(
notificationEvent.EventType,
((ItemEvent)notificationEvent).ItemId, ((ItemEvent)notificationEvent).ParentFolderId));
break;
default:
break;
}
}
}
}
}
public void Disconnect()
{
if (_connection.IsOpen)
_connection.Close();
}
private void NotificationReceived(ExchangeEventArgs e)
{
if (ExchangeEvent != null)
{
ExchangeEvent(this, e);
}
}
public void Dispose()
{
_disposing = true;
Dispose(_disposing);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing && !_disposed)
{
if (_connection != null)
{
if (_connection.IsOpen)
_connection.Close();
_connection = null;
}
_exchange = null;
_disposed = true;
}
}
}
I think I can answer this question myself - to guarantee continuous work you need to renew your subscription. But here is the weird part: there is no exception being thrown (there is an exception object in SubscriptionErrorEventArgs
in the OnDisconnect
event handler:
Unable to retrieve events for this subscription. The subscription must be recreated.
Just timeout when you will not get notified about subscription events and after some time you'll continue receiving event notification with an old subscription...
Update
The solution I ended up with: if SubscriptionErrorEventArgs
in the OnDisconnect
event does not contain an exception - simply open the closed connection, otherwise - raise the Disconnected
event, and let the parent class decide - should we try Reconnect
or not (by calling Reconnect
).
public class ExchangeEventArgs : EventArgs
{
public EventType Type { get; private set; }
public ItemId ItemId { get; private set; }
public FolderId FolderId { get; private set; }
public ExchangeEventArgs(EventType type, ItemId itemId, FolderId folderId)
{
Type = type;
ItemId = itemId;
FolderId = folderId;
}
}
public class ExchangeClient : IDisposable
{
private ExchangeService _exchange;
private SubscriptionBase _subscription;
private StreamingSubscriptionConnection _connection;
private bool _disposed;
private bool _disposing;
public event EventHandler<ExchangeEventArgs> ExchangeEvent;
public event EventHandler<DisconnectEventArgs> Disconnected;
public ExchangeClient(string userName, string password, string domain, ExchangeVersion version)
{
_exchange = new ExchangeService(version);
_exchange.Credentials = new WebCredentials(userName, password);
_exchange.AutodiscoverUrl(userName + "@" + domain);
_connection = new StreamingSubscriptionConnection(_exchange, 30);
CreateSubscription();
_connection.OnNotificationEvent += OnNotificationEvent;
_connection.OnSubscriptionError += OnSubscriptionError;
_connection.OnDisconnect += OnDisconnect;
_connection.Open();
}
private void CreateSubscription()
{
var ids = new FolderId[2] { new FolderId(WellKnownFolderName.Root), new FolderId(WellKnownFolderName.Inbox) };
var events = new List<EventType>();
events.Add(EventType.NewMail);
if (_subscription != null)
{
((StreamingSubscription)_subscription).Unsubscribe();
_connection.RemoveSubscription((StreamingSubscription)_subscription);
}
_subscription = _exchange.SubscribeToStreamingNotifications(ids, events.ToArray());
_connection.AddSubscription((StreamingSubscription)_subscription);
}
private void OnDisconnect(object sender, SubscriptionErrorEventArgs args)
{
if (args.Exception == null)
{
if (!_disposing && _connection != null)
{
_connection.Open();
}
}
else
{
if (Disconnected != null)
Disconnected(this, new DisconnectEventArgs("Exchange exception", args.Exception));
}
}
public bool Reconnect()
{
try
{
if (!_disposing && _connection != null)
{
CreateSubscription();
_connection.Open();
return true;
}
else
return false;
}
catch (Exception)
{
return false;
}
}
private void OnSubscriptionError(object sender, SubscriptionErrorEventArgs args)
{
OnDisconnect(sender, args);
}
private void OnNotificationEvent(object sender, NotificationEventArgs args)
{
if (_subscription != null)
{
if (args.Subscription.Id == _subscription.Id)
{
foreach (var notificationEvent in args.Events)
{
switch (notificationEvent.EventType)
{
case EventType.Status:
break;
case EventType.NewMail:
NotificationReceived(new ExchangeEventArgs(
notificationEvent.EventType,
((ItemEvent)notificationEvent).ItemId, ((ItemEvent)notificationEvent).ParentFolderId));
break;
default:
break;
}
}
}
}
}
public void Disconnect()
{
if (_connection.IsOpen)
_connection.Close();
}
private void NotificationReceived(ExchangeEventArgs e)
{
if (ExchangeEvent != null)
{
ExchangeEvent(this, e);
}
}
public void Dispose()
{
_disposing = true;
Dispose(_disposing);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing && !_disposed)
{
if (_connection != null)
{
if (_connection.IsOpen)
_connection.Close();
_connection = null;
}
_exchange = null;
_disposed = true;
}
}
}