Working with Streaming Notifications By Using the EWS Managed API

Microsoft Exchange Server 2010 Service Pack 1 (SP1) introduces streaming notifications, a new feature that combines push and pull notifications. For those of you who are not too familiar with notifications, I’ll give some background information. Pull notifications require your application to create a subscription and every now and then request an update from the server. This can create quite a bit of traffic between the client and server if you want to keep up-to-date with events on the server. Push notifications require you to write your own listener application. You create a push subscription with the server and when an event fires, it pushes the notification to your listener application. This works well because you don’t have to keep asking the server for updates, but it does require that you create a separate application to receive the notifications. Now, we have the best of both worlds with streaming notifications. After you have established your notification subscription, the connection remains open to allow the server to push notifications back to your application. You don’t have to request updates as for the pull subscription, and you don’t have to create a listener application as for the push notifications.

We wrote a console example for creating the subscription and handling notification responses. The first step in your application is to create a service binding to your Exchange mailbox. Because this is a pretty common routine in Microsoft Exchange Web Services (EWS) Managed API, I don’t want to go over it in detail here, but if you need a refresher check out the topic Connecting to EWS in the EWS Managed API SDK.

After the service binding is completed, call the SetStreamingNotifications function and pass in the service binding. In this example, a subscription is made to the Inbox and the notifications will be sent for new e-mail messages and for items that have been created or deleted in the Inbox. Because this is just an example, the timeout is set to one minute, but you can adjust this as necessary for your application. This example will also recognize the OnDisconnect event and ask if the connection should remain open. You can see that after the subscription is created, it’s very easy to reopen the connection. If you don’t want to reopen the connection, the code will close the connection object.

When a notification is sent back, the OnEvent function is called and a message is output to the console. When you get the notification, you also get the itemID that you can use to bind to the item and get additional information about that item.

The following code shows the console application…

static void SetStreamingNotifications(ExchangeService service)
{
    // Subscribe to streaming notifications on the Inbox folder, and listen
    // for "NewMail", "Created", and "Deleted" events.
    StreamingSubscription streamingsubscription = service.SubscribeToStreamingNotifications(
        new FolderId[] { WellKnownFolderName.Inbox },
        EventType.NewMail,
        EventType.Created,
        EventType.Deleted);

    StreamingSubscriptionConnection connection = new StreamingSubscriptionConnection(service, 1);

    connection.AddSubscription(streamingsubscription);
    // Delegate event handlers.
    connection.OnNotificationEvent +=
        new StreamingSubscriptionConnection.NotificationEventDelegate(OnEvent);
    connection.OnSubscriptionError +=
        new StreamingSubscriptionConnection.SubscriptionErrorDelegate(OnError);
    connection.OnDisconnect +=
        new StreamingSubscriptionConnection.SubscriptionErrorDelegate(OnDisconnect);
    connection.Open();

    Console.WriteLine("--------- StreamSubscription event -------");
}

static private void OnDisconnect(object sender, SubscriptionErrorEventArgs args)
{
    // Cast the sender as a StreamingSubscriptionConnection object.          
    StreamingSubscriptionConnection connection = (StreamingSubscriptionConnection)sender;
    // Ask the user if they want to reconnect or close the subscription.
    ConsoleKeyInfo cki;
    Console.WriteLine("The connection to the subscription is disconnected.");
    Console.WriteLine("Do you want to reconnect to the subscription? Y/N");
    while (true)
    {
        cki = Console.ReadKey(true);
        {
            if (cki.Key == ConsoleKey.Y)
            {
                connection.Open();
                Console.WriteLine("Connection open.");
                break;
            }
            else if (cki.Key == ConsoleKey.N)
            {
                // The ReadKey in the Main() consumes the E.
                Console.WriteLine("\n\nPress E to exit");
                break;
            }
        }
    }
}

static void OnEvent(object sender, NotificationEventArgs args)
{
    StreamingSubscription subscription = args.Subscription;

    // Loop through all item-related events.
    foreach (NotificationEvent notification in args.Events)
    {

        switch (notification.EventType)
        {
            case EventType.NewMail:
                Console.WriteLine("\n-------------Mail created:-------------");
                break;
            case EventType.Created:
                Console.WriteLine("\n-------------Item or folder created:-------------");
                break;
            case EventType.Deleted:
                Console.WriteLine("\n-------------Item or folder deleted:-------------");
                break;
        }
        // Display the notification identifier.
        if (notification is ItemEvent)
        {
            // The NotificationEvent for an e-mail message is an ItemEvent.
            ItemEvent itemEvent = (ItemEvent)notification;
            Console.WriteLine("\nItemId: " + itemEvent.ItemId.UniqueId);
        }
        else
        {
            // The NotificationEvent for a folder is an FolderEvent.
            FolderEvent folderEvent = (FolderEvent)notification;
            Console.WriteLine("\nFolderId: " + folderEvent.FolderId.UniqueId);
        }
    }
}
static void OnError(object sender, SubscriptionErrorEventArgs args)
{
    // Handle error conditions.
    Exception e = args.Exception;
    Console.WriteLine("\n-------------Error ---" + e.Message + "-------------");
}

To make this application work properly, bind it to your Exchange mailbox and call the SetStreamingNotifications function. Then send a message to this mailbox from an e-mail client of your choice — just do it before the one minute timeout occurs. You will then get a message that an item was created and you have a new e-mail message in your Inbox.