Copying a Contact in Exchange is one thing synchronizing is another this can be one of the great contradictions and issues that faces anybody looking at synchronizing contacts between different storage mediums. Whether that be a Database, CSV file or other Mail System underlying its the same information stored and accessed in a different way. Because Exchange isn't your normal flat file database more a relatively complex property store that uses some complex datatypes doing property level synchronization does provide a unique set of challenges. Exchange Web Service presents some of these properties in more workable format but apart from this doesn't give a lot of help is solving this fundamental issue. The SyncFolderItem and notification operations only provide an Item level ability to spot changes on an item so the brave souls who want to embark on a greater level of synchronization must take this challenge unto themselves.
If your going to compare two contacts it first helps to have them in the same format there are a few methods of doing this one way you might go about this if your using Exchange 2007 and SQL 2005 is using something like http://msdn.microsoft.com/en-gb/library/bb508823.aspx. The path I've gone down it to convert them into the ContactType class that's used in EWS. This may or may not be the best method but it does have a flexibility that I'm after. With flexibility comes a great ability to do some really cool things.
So what I've come up with is a method that takes two ContactType objects as parameters and then returns a generic list of SetItemFieldType updates that can then be used direct in a UpdateItem Operation. With EWS you need to have a separate SetItemFieldType for each property of an item you want to update. If you have a large number of properties you are updating on an Item especially if they are different datatypes then these requests can get lengthy and complex. The first contact object represents the source object which has the new information you want synchronized and the second is destination object which you want updated. The changes that are returned are the differences on the source object when compared against the destination object. Because Contacts are made up of complex datatypes the compare class needs to deal differently depending on which property you are looking at.
String Properties: For string properties a simple comparison is done to work out if one property is different from the other.
String Arrays: For String arrays the elements of the arrays are joined and then the strings of the joined array is compared.
Email Addresses, Phone Numbers, Street Addresses: These are special indexed arrays so to compare the index valued two hashtables are used. The Hashtables allow the ability to do string comparison of the values based on the on the indexed values. With Street Addresses because of the way the indexing works with these properties nested hash tables are used.
Extended Properties:Extended Mapi properties are one of the things that make objects in Exchange hard to deal with. The class will go through any that are set in the source contact and that are retrieved in the destination object and and will do a comparison based on either the propertyID or propertyTag. Because there is no real good method to get all the Extended properties that have been set on a item unless you use ExMapi this is one area where you can and will loose fidelity on items you might be synchronizing because each of these properties must be explicitly specified if you want to synchronize them.
Because the SetItemUpdate will be different for each of these types there is a separate method that handles building the update.
Whats missing - This helper class will look for whats been added or changed in a contact and produce an update for those properties. But it wont produce DeleteItemFieldType to delete properties that may have been removed in the source contact.
How to use this : Watch this space i'll have a few sample over the next couple of weeks on how to use this but i just wanted to introduce the code in a seperate post. I've put a download of the code I've talked about here the actual code is too large to post.