none
EWS Managed API SyncFolderItems How to Update and Delete/Keeping track of items.

    Question

  • Hi, 

    I am building an app that is required to live in a trusted domain, monitor a collection of mailbox-calendars on an exchange server in that domain, and sync appointments to different mailboxes on one or many other servers.  The mailbox it is synced with is defined in an internal mapping table (sqlce) that is maintained by the user of this application.

    The problem I have is I can not work out a way to keep track of the remote appointment so that I can update or delete it if necessary.  After I create the appointments on the remote server they have a new itemid which does not correspond to the one returned by the sync folder items call on the local exchange server.  I can't find the item by start time/subject as these may have been changed or deleted.

    My sync method is below - am I going about this entirely the wrong way or is there a better way to user the SyncFolderItems method?

    The best approach I have come up with so far to get around my problem is to save ItemID of the remote appointment into a property of the local appointment but even this I am not sure will work because I don't know what properties are maintained after a delete? Please Help!!!!



    Cheers

    Lucas


    Tuesday, June 26, 2012 10:16 AM

Answers

  • I would use Extended Property because you can rely on that always being the CleanGlobalObjectId (while the ICalUid may or may not) and you will be able to use it reliability in a search also.

     One of the problems with SyncFolderItems (as well as EWS notifications and the WebDAV replication stuff that proceeded this in 2003) is that with deletes you only get a limited subset of information returned and there is nothing you can do to get more information so you have to work with what you get.

      There are few ways of approaching it if you store the EWSId along with the CleanGlobalObjectId you should be able to relate the EWSId that you get from SyncFolderItems when a delete happens then you can search for the deleted appointment based on the CleanGlobalObjectId.

    Cheers
    Glen

    Tuesday, July 24, 2012 6:04 AM

All replies

  • If your going to track and collate appointments then you would be better to use the  CleanGlobalObjectId have a read of http://blogs.msdn.com/b/mstehle/archive/2009/09/02/ews-uid-not-always-the-same-for-orphaned-instances-of-the-same-meeting.aspx . Although you won't be able to bind to this Id directly its something that doesn't change and its job is to act as Global Identifier for the appointment so you should be able to use it in a Search (even if you just trawling the Dumpster for deleted Items). The ItemId can change for a number of reasons and will certainly change once the Item is deleted.

    Cheers
    Glen

     
    Wednesday, June 27, 2012 6:45 AM
  • Hi Glen, 

    Thanks heaps for your reply! Sorry I am so slow to implement it, send it to my client, and then realise it is borked still!  I went with the storing the ICalUid (which I think IS the CleanGlobalObjectId you mentioned after I dissected the blogs you posted) in my database.  I stored the ICalUid of the appointment on the server, and the ICalUid of the one I created on the client in it's SQLCE database.  This allowed me to find one from the other for updates and deletes or so I thought.  Updates are fine.  But I still cannot find the deleted appointment on the server, or get enough info from the server, to find the right item to delete on the client.  I can no longer access the ICalUid of the appointment on the server because first I have to find the appointment in deleteditems or recoverabledeletions folder to get the ICalUid (or CleanGlobalObjectId).  SyncFolderItems returns the subject but not startdate so I cannot search in deleted etc using that combo and I don't feel subject alone will be reliable enough at all.  So, what you proposed works beautifully, but not for my initial problem, which was deleting.  

    Maybe this is microsoft fault but SyncFolderItems does not seem to return enough info to perform an accurate delete of an item.  If it helps at all I am following this example: http://msdn.microsoft.com/en-us/library/exchange/ee693003(v=exchg.80).aspx but of course the good folks at MS haven't filled in the important bits!


    LucasF

    Monday, July 23, 2012 10:10 AM
  • I would use Extended Property because you can rely on that always being the CleanGlobalObjectId (while the ICalUid may or may not) and you will be able to use it reliability in a search also.

     One of the problems with SyncFolderItems (as well as EWS notifications and the WebDAV replication stuff that proceeded this in 2003) is that with deletes you only get a limited subset of information returned and there is nothing you can do to get more information so you have to work with what you get.

      There are few ways of approaching it if you store the EWSId along with the CleanGlobalObjectId you should be able to relate the EWSId that you get from SyncFolderItems when a delete happens then you can search for the deleted appointment based on the CleanGlobalObjectId.

    Cheers
    Glen

    Tuesday, July 24, 2012 6:04 AM
  • Hi Glen, 

    Thanks again.  It sounds like I wasn't using the CleanGlobalObjectId then, I think I must have gotten on the wrong track with that a while back.  I will have another look at how to do that properly now.

    Re your suggestion of also storing the EWSId which sounded good, is at as simple as this to get the EWSId?:

                        else if (ic.ChangeType == ChangeType.Delete)
                        {
                            AlternateId OriginalId = new AlternateId(IdFormat.EntryId, ic.ItemId.UniqueId, LocalMailbox,true);
                            AlternateId EWSID = LocalExchangeService.ConvertId(OriginalId, IdFormat.EwsId) as AlternateId;

    Cheers

    Lucas


    LucasF

    Tuesday, July 24, 2012 9:38 AM
  • The "ic.ItemId.UniqueId" from the ItemChange is the EWSId for the Item that was deleted so you dont need to convert it

    Cheers
    Glen

    Wednesday, July 25, 2012 6:46 AM
  • LOL! Excellent, I spent three hours trying to convert what I wanted...into something I didn't!!!!

    I am still stuck trying to correctly read the CleanGlobalObjectId.  I have come up with the below, however the output parameter is always NULL.  I have tried changing the exchange version I am targeting, tried rebinding to the appointment after it was saved, but I am always getting NULL.  Below is not from my app, it's just a quick test I set up so I could try and understand how to do this properly.

    Any idea why below might be returning NULL?

    Appointment testApp = new Appointment(LocalExchangeService);
    testApp.Subject = "Lucas is testing things";
    testApp.Start = DateTime.Parse("30/07/2012 1:00PM");
    testApp.End = DateTime.Parse("30/07/2012 2:00PM");
    testApp.Save();
    object rsCleanGlobalObjectId = null;
    ExtendedPropertyDefinition cgoid = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.Appointment, 0x23, MapiPropertyType.Binary);
    PropertySet cgoidPropSet = new PropertySet(BasePropertySet.FirstClassProperties);
    cgoidPropSet.Add(cgoid);
    testApp.Load(cgoidPropSet);
    testApp.TryGetProperty(cgoid, out rsCleanGlobalObjectId);

    Cheers
    Lucas


    LucasF

    Sunday, July 29, 2012 8:17 AM
  • Your property definition is wrong you should be using

    ExtendedPropertyDefinition cgoid = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.Meeting, 0x23, MapiPropertyType.Binary);
    Cheers
    Glen
    Monday, July 30, 2012 6:54 AM
  • Just want to say thanks for all your help Glen.  Using the CleanGlobalObjectId I now have a very reliable tool that can sync appointments across any number of exchange servers. Cool stuff!  I am about to post a new thread which I hope you will spot, I have just been asked to develop the same sort of tool but for Contacts instead of appointments.  

    LucasF

    Wednesday, August 8, 2012 6:12 AM
  • Hi Lucas,

    If I may suggest, we have same kind of application. Multiple users (with Exchange Email) and keep the appointments synchronized. We use an timer to pull each 30 seconds changes from Exchange Server and store them in a queue (database). We have an own calendar component so want a "connection" between the appointments.

    We store the uniqueId from an Exchange appointment in our database. When I receive an create/update I take a look if I can find the "connection record". If found, update the correct appoint, otherwise (when create, do nothing) update the appointment.

    When I get an appointment (create) from the organizer, i create only that appointment and update all the uniqueId's from the attendees. (So I keep in track where everything is coupled with). Updating the appointmens of the attendees works with the solution of Glen. Get the ICal and impersonate all the users and search via ICal for the appointment.

    Thursday, August 9, 2012 7:45 AM
  • Hi Glen, my apologies but I must be missing something.   I'm using EWS with Exchange 2007 and my application which tracks a series of appointments.   So far I'm just working with bringing in appointments created in Exchange into my application.   I'm using the SyncFolderItems and cycling through the ChangeItemCollection.   Everything works nicely for ChangeType.Create and ChangeType.Update.    However, if I delete an appointment in Exchange, I have no idea how to get any further details including the CleanGlobalObjectId.    I've managed to store the CleanGlobalObjectId with my appointments in my application but that's only when I actually have an exchange appointment to work with.   On deletes, all I'm given in the ChangeItemCollection iteration is the ic.ItemId.   The ic.Item itself is null.   I've read everywhere not to use the ItemId and that's fine but given the scenario of all you have is an ItemId, how would you locate the CleanGlobalObjectId or any other property for that matter.   I'm not quite sure how to search for a deleted appointment based on any criteria as I don't seem to have any.     
    Thursday, November 6, 2014 4:13 PM
  • Basically you need to track both the ItemId and the CleanGlobalObjectId, this is because with Notifications or any of the subscription operations the only thing you will be given as information is the ItemId  of the Item that was deleted. So if you have both tracked you should then be able to relate that back to the CleanGlobalObjectId which is a property you can then search on. (the ItemId is useless once the Item is remove)

    The other way is just trawl the dumpster and look the Item's Modified time with 2007 you still have the old dumpster so the Item maybe in one of three locations eg Calendars dumpster, DeletedItems or the DeletedItems dumpster depending on how it was deleted..

    The other problem with 2007 is that with EWS you can only use FindItems to do SoftDeleted traversal of the dumpster you won't be able to do GetItem etc as this functionally was added in 2010.

    Cheers
    Glen

    Friday, November 7, 2014 12:14 AM
  • Hey Glen,

    Im trying to track deleted appointments as well. I can track the ItemId of and item, but I noticed the Id returned with the ItemChange object on SyncFolderItems is different than the Id of the appointment before the delete. So how can I use the returned ItemChange.ItemId to figure out what appointment was really deleted.

    Is my only option to go through the deleted items?

    Thanks in advanced,
    Danny

    Thursday, August 2, 2018 9:39 PM