user1475331 user1475331 - 1 year ago 109
ASP.NET (C#) Question

Email Conversations / Email Threading support in EWS Managed Api (against Exchange 2010 or so)

The EWS Managed API has a handful of functions for retrieving and managing email conversations (aka email threads). Unfortunately, a large part of them work only against new versions of Exchange (2013 etc.)

Outlook does implement email threading against older versions of Exchange. Perhaps it does this by managing the threads by itself (Outlook is a desktop application, the emails are copied on the local machine, therefore they can be easily grouped by Conversation Topic etc.).

Now, how can email threading be supported within a web application? What is usually done for supporting this feature in an Exchange client? By supported I mean:

  • retrieve first 10 conversations snapshots, then retrieve the next 10 conversations snapshots - that is, support for paging (retrieve pages on demand) - this data would be used to build a master view

  • retrieve all emails within a conversation - that is, retrieve the children of a conversation on demand) - this data would be used to build a detail view of a conversation.

Issues with EWS Managed Api etc.:

  • there is no Conversation.Bind(conversationId) in EWS Managed API

  • ExchangeService.FindItems(filter for ConversationTopic == "some topic") is by no means reliable (because there may be different conversations having the same topic)

  • ExchangeService.FindItems(filter for ConversationId == "QWERYUIO") - I could not figure out how to use this :) Is it possible to search for emails by ConversationId?

  • functions like ExchangeService.GetConversationItems() are only "applicable for clients that target Exchange Online and versions of Exchange starting with Exchange Server 2013."

What I am using now (as a workaround):

  1. retrieve (on demand) a page of conversations using ExchangeService.FindConversation()

  2. for each conversation in the retrieved page, read the Conversation.GlobalIds property

  3. build an aggregate (an array) containing the values from all GlobalIds - by concatenating Conversation.GlobalIds of all the conversations

  4. make an Exchange call to bind the ids to emails (ExchangeService.BindToItems)

  5. perform a group-by operation of the emails (conceptually, it is a grouping operation, but implementation is not a trivial group-by call - emails cannot be grouped by ConversationId, as that property is not available when working against Exchange 2010, though the documentation does not specify this)

  6. use the data to build the UI in one step (the list of conversations for the master view, the groups of emails for the detail view of each conversation) etc.

Some issues with the implementation described above

  • I am retrieving a lot of data from the server when calling the ExchangeService.BindToItems operation - the performance is not excellent, but it is not quite bad either. Of course, it would have been better to retrieve the emails only when the user wants to access the detail view of a specific conversation. A possible hack: hold the GlobalIds array somewhere in a hidden field, then use it to fetch the emails in order to build the detail view. I know that a GET request is limited in size, but however...

On email conversations / email threading, nobody knows which supports what:

  • Here it says that FindConversation(ViewBase, FolderId) is applicable for clients that target Exchange Online and versions of Exchange starting with Exchange Server 2013. On the other hand, here is written that the ExchangeService.FindConversation() function can be used for Versions of Exchange starting with Exchange 2010, including Exchange Online.

  • This is funny, too: Applies to: EWS Managed API | Exchange Server 2010 Service Pack 1 (SP1), BUT Ensure that you have an Exchange 2013 or Exchange Online service account with a major version of 15 or higher. :)

  • Here it says that the Item.ConversationId property is available in Versions of Exchange starting with Exchange 2010, including Exchange Online. But it is not :)

Note: I am not very sure about the support of the Item.ConversationId, as I do not have the code at hand and cannot perform a test right now. Therefore, please forgive me if that property is available after all when using EWS Managed API against Exchange 2010.

All in all,
do you have any ideas for implementing the email conversations / email threading feature in a web application, using the EWS Managed API against an Exchange 2010 server?

Thank you a lot for having the patience to read such a long post :)

Some references:

Implementing Outlook 2010's group by conversation using EWS and Exchange 2007
Exchange Webservice Managed API - Find items by extended properties

Answer Source

I've addressed some of the documentation questions you had in the comments, so I'm going to attempt to answer your real coding questions here.

To get your master view, ExchangeService.FindConversation is the right method to use. It does support paging by limiting the results to the number of conversations specified by the view parameter. You could call it on demand to get older and older results.

To get your detailed view, because ExchangeService.GetConversationItems isn't available on Ex2010, you can use ExchangeService.FindItems with an IsEqualTo SearchFilter that searches for items with a matching ConversationId (see code below). There's more information about search filters here: How to: Use search filters with EWS in Exchange.

In the following method, I limited the properties of the FindItems call by specifying a property set, and not returning all the properties. If you wanted to return all the properties, you would just remove the line that sets the PropertySet.

static void forumFindConversationItem(ExchangeService service)
        ItemView view = new ItemView(10);

        //Remove the following line if you want to get all the properties for each message. This will limit the properties returned in your results (and save time).
        view.PropertySet = new PropertySet(EmailMessageSchema.Subject, EmailMessageSchema.DateTimeReceived);

        SearchFilter.IsEqualTo conversationFilter =
            new SearchFilter.IsEqualTo(EmailMessageSchema.ConversationId, "AAQkADIwM2ZlM2ZlLWMwYjctNDg2Ny04MDU0LTVkMTFmM2IxY2ZjZQAQANEDR7V/30dphLiNOLSTuxE=");

        FindItemsResults<Item> results = service.FindItems(WellKnownFolderName.Inbox, conversationFilter, view);

Once you have each ItemID (returned by the code above) you could use the Bind method to get all the properties on each item.

Hope that helps. I'll follow up when the versioning issues for the methods on MSDN have been updated.