Search code examples
c#exchange-serverexchangewebservicesews-managed-apioutlook-calendar

EWS Managed Api - Retrieve calendar items only once


I've got a windows service to read calendar items from Exchange and store them in my application's DB against each user. Now I wonder what is the best way to mark an item on Exchange to not be retrieved in the next service run after storing it?

I tried to add an extended property to the calendar item and set it's value and then search based on it's value each time:

DateTime startDate = DateTime.Now;
DateTime endDate = startDate.AddDays(60);
const int NUM_APPTS = 60;    
// Set the start and end time and number of appointments to retrieve.
CalendarView cView = new CalendarView(startDate, endDate, NUM_APPTS);

Guid myPropertyId = new Guid("{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}");
ExtendedPropertyDefinition myExtendedProperty = new ExtendedPropertyDefinition(myPropertyId, "SyncFlag", MapiPropertyType.Boolean);

// Limit the properties returned to the appointment's subject, start time, end time and the extended property.
 cView.PropertySet = new PropertySet(AppointmentSchema.Subject, AppointmentSchema.Start, AppointmentSchema.End, AppointmentSchema.Location, myExtendedProperty);

// Retrieve a collection of appointments by using the calendar view.
List<SearchFilter> searchFilterCollection = new List<SearchFilter>();
searchFilterCollection.Add(new SearchFilter.IsEqualTo(AppointmentSchema.Start, DateTime.Now));
searchFilterCollection.Add(new SearchFilter.IsEqualTo(AppointmentSchema.End, DateTime.Now.AddDays(60)));

searchFilterCollection.Add(new SearchFilter.IsNotEqualTo(myExtendedProperty, true)); //Do not fetch already marked calendar items

SearchFilter searchFilter = new SearchFilter.SearchFilterCollection(LogicalOperator.And, searchFilterCollection);

FindItemsResults<Item> appointments = service.FindItems(WellKnownFolderName.Calendar, searchFilter, cView);

Searching calendar items using this code throws this exception:

Restrictions and sort order may not be specified for a CalendarView.

And I'm not sure if it's the best way to do it either. Any idea?

Thanks


Solution

  • One way to this is to use the SyncFolderItems method. It will return the items that were changed since the last call. All you have to do is save a synchronization "cookie" between each call.

    You should also filter out the appointment items that were created since your last call.

    This below code snippet should do the trick.

    static private void GetChangedItems()
    {
        // Path to cookie
        string syncStateFileName = @"c:\temp\ewssyncstate.txt";
    
        try
        {
            var service = GetServiceAP();
            bool moreChangesAvailable = false;
    
            do
            {
                string syncState = null;
    
    
                if (File.Exists(syncStateFileName))
                {
                    // Read cookie
                    syncState = File.ReadAllText(syncStateFileName);
                }
    
                ChangeCollection<ItemChange> icc = service.SyncFolderItems(new FolderId(WellKnownFolderName.Calendar), PropertySet.FirstClassProperties, null, 10, SyncFolderItemsScope.NormalItems, syncState);
    
                if (icc.Count == 0)
                {
                    Console.WriteLine("There are no item changes to synchronize.");
                }
                else
                {
                    syncState = icc.SyncState;
    
                    // Save cookie
                    File.WriteAllText(syncStateFileName, syncState);
    
                    moreChangesAvailable = icc.MoreChangesAvailable;
    
                    // Only get appointments that were created since last call
                    var createdItems = icc
                        .Where(i => i.ChangeType == ChangeType.Create)
                        .Select(i => i.Item as Appointment)
                        ;
    
                    if (createdItems.Any())
                    {
                        service.LoadPropertiesForItems(createdItems, PropertySet.FirstClassProperties);
    
                        foreach (Appointment appointment in createdItems)
                        {
                            Console.WriteLine(appointment.Subject);
                        }
                    }
                }
            }
            while (moreChangesAvailable);
    
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
    }