ExchangeService.FindItems() - c#

I've been requested to create an application that should create a common base from 3 different email addresses sources and update each base taking the most common updated dataset.
Among the three sources I have an Exchange server Contacts addressbook. I know that I can access such data trough EWS and in specific I should probably use the ExchangeService.FindPeople() method or maybe FindPersona().
This should work but, since I'm looking only for new/updated contacts, it will load significantly the server (maybe not for new records but I cannot understand how to retrieve update records) and this it's not a good practice.
I've found in MSDN docs a way to be notified on updates on message base but there's nothin related on updates on contacts:
https://msdn.microsoft.com/en-us/library/office/dn458791(v=exchg.150).aspx
Is there something to be notified on updates on contacts (even trough third party products/APIs).
P.S. I would like to code in C# (or other .NET languages) but I'm open to anything else.

You should be able to check for newly created contacts by iterating through the contacts and accessing the DateTimeCreated property on each.
To check for updated contacts, you could use the LastModifiedTime property.
// Get the number of items in the Contacts folder.
ContactsFolder contactsfolder = ContactsFolder.Bind(service, WellKnownFolderName.Contacts);
// Set the number of items to the number of items in the Contacts folder or 50, whichever is smaller.
int numItems = contactsfolder.TotalCount < 50 ? contactsfolder.TotalCount : 50;
// Instantiate the item view with the number of items to retrieve from the Contacts folder.
ItemView view = new ItemView(numItems);
// To keep the request smaller, request only the DateTimeCreated and LastModifiedTime properties.
view.PropertySet = new PropertySet(BasePropertySet.IdOnly, ContactSchema.DateTimeCreated, ContactSchema.LastModifiedTime);
// Retrieve the items in the Contacts folder that have the properties that you selected.
FindItemsResults<Item> contactItems = service.FindItems(WellKnownFolderName.Contacts, view);
// Display the list of contacts.
foreach (Item item in contactItems)
{
if (item is Contact)
{
Contact contact = item as Contact;
if (contact.DateTimeCreated.Date == DateTime.Today.Date)
{
//Notify - Newly created contact
}
if (contact.LastModifiedTime.Date == DateTime.Today.Date)
{
//Notify - Newly modified contact
}
}
}

This is simple example to find contact by nikname. You can find a more advaced search here.
// Create a view with a page size of 1.
ItemView view = new ItemView(1);
// Create the search filter.
SearchFilter searchFilter = new SearchFilter.IsEqualTo(ContactSchema.NickName, "806555335");
FindItemsResults<Item> contactItems = service.FindItems(WellKnownFolderName.Contacts, searchFilter, view);
if(contactItems != null && contactItems.Count()>0){
//contact found!
Contact contact = contactItems.First() as Contact;
}

Related

Trouble reading contact.addresses in EWS. All contacts read display "The given key was not present in the dictionary."

Can't get the first email address to display. All records display like this:
"The given key was not present in the dictionary". There are definately email addresses associated with the contacts. Here is the code
....
static void Reademail()
{
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013);
service.UseDefaultCredentials = true;
service.AutodiscoverUrl("validaddress#ddd.com");
// Get the number of items in the contacts folder. To limit the size of the response, request only the TotalCount property.
ContactsFolder contactsfolder = ContactsFolder.Bind(service,WellKnownFolderName.Contacts,new PropertySet(BasePropertySet.IdOnly, FolderSchema.TotalCount));
// Set the number of items to the number of items in the Contacts folder or 50, whichever is smaller.
int numItems = contactsfolder.TotalCount < 50 ? contactsfolder.TotalCount : 50;
// Instantiate the item view with the number of items to retrieve from the Contacts folder.
ItemView view = new ItemView(numItems);
// To keep the request smaller, request only the display name property.
view.PropertySet = new PropertySet(BasePropertySet.IdOnly, ContactSchema.DisplayName);
// Retrieve the items in the Contacts folder that have the properties that you selected.
FindItemsResults<Item> contactItems = service.FindItems(WellKnownFolderName.Contacts, view);
// Display the list of contacts.
foreach (Item item in contactItems)
{
if (item is Contact)
{
Contact contact = item as Contact;
Console.Write(contact.DisplayName + " ");
try
{
Console.WriteLine(contact.EmailAddresses[EmailAddressKey.EmailAddress1].Name);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
Console.ReadKey();
}
.....
the fix I found was to use the contact.Load statement. Although maybe not the most efficient as it mask a call back to EWS. Apparently this loads the primary class and the email addresses are then visible.

Physical address order of Zip/City

I am using EWS to add contact items to an office365 account.
Everything works fine, just one detail is not as expected.
When I create a new contact and add e.g. the home address like this:
if (ewsContact.PhysicalAddresses.Contains(PhysicalAddressKey.Home) == false)
{
ewsContact.PhysicalAddresses[PhysicalAddressKey.Home] = new PhysicalAddressEntry();
}
if (string.IsNullOrEmpty(contact.HomeZip) == false)
{
ewsContact.PhysicalAddresses[PhysicalAddressKey.Home].PostalCode = contact.HomeZip;
}
if (string.IsNullOrEmpty(contact.HomeCity) == false)
{
ewsContact.PhysicalAddresses[PhysicalAddressKey.Home].City = contact.HomeCity;
}
The data is written to the contact item, but in the combined field and on the card view in Outlook the ordering of Zip and City is always the order that is used in the US - e.g. Washington 98155
As I have a lot of addresses from europe, I need the correct order - e.g. 10115 Berlin.
If I open the contact in Outlook, change the Zip code at one position and save it back, the order is saved correctly and the display in Outlook is correct.
Is there any way to have the correct order with EWS?
You will need to set the PidLidWorkAddress property https://msdn.microsoft.com/en-us/library/office/cc815905.aspx which should contain the address information formatted in the locale of the client. So in EWS you need to set this using the Extended property definition eg
ExtendedPropertyDefinition PidLidWorkAddress = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.Address, 0x801B, MapiPropertyType.String);
ewsContact.SetExtendedProperty(PidLidWorkAddress, AddressValue);

Why does TotalCount property on public folder always return 0 items?

Using Exchange 2013 SP1 and Exchange Web Services managed API 2.2 to try and get a list of the contacts in a contact folder that I have stored in Public Folders. I would like to limit the size of the ItemView to the total number of contacts that I have in this contact folder, but when I try to return that property (contactfolder.TotalCount) it always returns 0. If I try this with using the Contact folder under my mailbox a value other than 0 is returned. I can get around this issue by either specifying a value for the constructor for the ItemView as either a specific number or by using int.MaxValue, but I would rather use the total number of items that are in the contact list. Any assistance is greatly appreciated! Here is the relevant code:
private FindItemsResults<Microsoft.Exchange.WebServices.Data.Item> ExchangeContacts()
{
// Setup the exchange server connection.
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
service.AutodiscoverUrl("someone#mydomain.com");
// Set the filter to choose the correct contact list
SearchFilter filter = new SearchFilter.IsEqualTo(FolderSchema.DisplayName, "My Public Contacts");
SearchFilter.SearchFilterCollection filterCollection = new SearchFilter.SearchFilterCollection();
filterCollection.Add(filter);
// Get the FolderId using the search filter.
Folder parent = Folder.Bind(service, WellKnownFolderName.PublicFoldersRoot);
FindFoldersResults results = parent.FindFolders(filter, new FolderView(1));
FolderId fid = results.Single().Id;
// Get the Contact folder based on the folderid.
ContactsFolder contactsfolder = (ContactsFolder)results.Single();
ItemView view = new ItemView(contactsfolder.TotalCount);
// Set the property that need to be shown in the page.
view.PropertySet = new PropertySet(BasePropertySet.IdOnly, ContactSchema.DisplayName, ContactSchema.CompanyName, ContactSchema.LastModifiedTime, ContactSchema.BusinessAddressCity, ContactSchema.BusinessAddressPostalCode, ContactSchema.BusinessAddressState, ContactSchema.BusinessAddressStreet, ContactSchema.HomeAddressCity, ContactSchema.HomeAddressPostalCode, ContactSchema.HomeAddressState, ContactSchema.HomeAddressStreet, ContactSchema.ItemClass, ContactSchema.FileAs, ContactSchema.LastModifiedName);
//view.PropertySet = new PropertySet(BasePropertySet.IdOnly, ContactSchema.DisplayName);
// Order the results by one of the selected properties
//view.OrderBy.Add(ContactSchema.LastModifiedTime, Microsoft.Exchange.WebServices.Data.SortDirection.Descending);
FindItemsResults<Microsoft.Exchange.WebServices.Data.Item> contactItems = contactsfolder.FindItems(view);
return contactItems;
}
I recreated your public folder (immediately under the public folder root), added a contact to it, and ran your code and get a contactItems.TotalCount value of 1 (as expected). However, after discussing this with the Exchange product team I learned that FindFolder can return an incorrect value if the FindFolder request gets routed to the public folder mailbox that doesn't have the content of the public folder. So TotalCount can return an incorrect value and is not supported for public folders. We will get the documentation updated to reflect this issue.
FindFolder operation (Exchange 2013):
Using the Default value for the BaseShape, the response returns the
folder name, the folder ID, the number of subfolders, the number of
child folders found in the folder, and the count of unread items.
<...>
FindFolder responses to a request with the AllProperties response
shape will not return the TotalCount and UnreadCount elements for
public folder searches.
Guess you need to specify a property filter in the search criteria.

casting objects in c# (Exchange Web Services Item to Email)

I cannot work out how I can do what I need to do.
I have a method called 'MarkAsRead' that takes an ItemID and should mark the mail item as read.
But it seems that the Item doesn't have an 'IsRead' proeprty, only an email does, so I need to cast my Exchange WebServices mail item to an email message.
here is the code:
try
{
System.Diagnostics.Debugger.Break();
//creates an object that will represent the desired mailbox
Mailbox mb = new Mailbox(common.strInboxURL);
//creates a folder object that will point to inbox fold
FolderId fid = new FolderId(WellKnownFolderName.Inbox, mb);
//this will bind the mailbox you're looking for using your service instance
Microsoft.Exchange.WebServices.Data.Folder inbox = Microsoft.Exchange.WebServices.Data.Folder.Bind(service, fid);
//// if the property is not loaded yet, first load it
//mail.Load(PropertySet(BasePropertySet.IdOnly, EmailMessageSchema.IsRead));
//if (!mail.IsRead) // check that you don't update and create unneeded traffic
//{
// mail.IsRead = true; // mark as read
// mail.Update(ConflictResolutionMode.AutoResolve); // persist changes
//}
// As a best practice, limit the properties returned to only those that are required.
PropertySet propSet = new PropertySet(BasePropertySet.IdOnly, ItemSchema.Subject);
// Bind to the existing item by using the ItemId.
// This method call results in a GetItem call to EWS.
Item item = Item.Bind(service, itemId, propSet);
EmailMessage mail = Item.Bind(service, itemId, propSet);
item.Load(new PropertySet(BasePropertySet.IdOnly,EmailMessageSchema.IsRead));
item.IsRead = true;
item.Update(ConflictResolutionMode.AlwaysOverwrite);
return true;
}
I am trying to do something like this:
// if the property is not loaded yet, first load it
mail.Load(PropertySet(BasePropertySet.IdOnly, EmailMessageSchema.IsRead));
if (!mail.IsRead) // check that you don't update and create unneeded traffic
{
mail.IsRead = true; // mark as read
mail.Update(ConflictResolutionMode.AutoResolve); // persist changes
}
unfortunately I need to be able to get a unique email message from the item.
how can I do that please?
Philip-
You can add EmailMessageSchema.IsRead to your property set, so that you can get it in your Bind call, and then you don't have to call Load at all.
PropertySet propSet = new PropertySet(BasePropertySet.IdOnly,
ItemSchema.Subject, EmailMessageSchema.IsRead);
The EmailClass derives from the Item class, so Bind works on both. So you can do the following:
EmailMessage mail = EmailMessage.Bind(service, itemId, propSet);
Put that together and you've got this:
PropertySet propSet = new PropertySet(BasePropertySet.IdOnly, ItemSchema.Subject, EmailMessageSchema.IsRead);
EmailMessage mail = EmailMessage.Bind(service, itemId, propSet);
if (!mail.IsRead) // check that you don't update and create unneeded traffic
{
mail.IsRead = true; // mark as read
mail.Update(ConflictResolutionMode.AutoResolve); // persist changes
}
Philip,
When testing my code I added just a little bit to the Item.Bind() from your code to make this work:
EmailMessage mail = Item.Bind(service, itemId, propSet) as EmailMessage;
The as EmailMessage will cast it to the appropriate type for you. After that I was able to see the isRead property.
I hope this helps. If this does resolve your problem, please mark the post as answered.
Thanks,
--- Bob ---

How to retrieve a Contact's Keywords with Tridion's Outbound Email API?

I'm using the Tridion.OutboundEmail.ContentManagement API to retrieve and manage contact details.
Retrieving Contacts is working fine, as is pulling back the ExtendedDetails dictionary, but the Keywords TcmUriCollection is always empty.
[Test]
public void GetContacts_via_address_book()
{
var uri = new TcmUri(101, 2, TcmItemTypes.StaticAddressBook);
var addressBook = new StaticAddressBook(uri);
var contacts = addressBook.GetContacts();
foreach (var contact in contacts)
{
var firstName = contact.ExtendedDetails["NAME"].StringValue;
Assert.That(contact.EmailAddress, Is.Not.Empty); // PASS
Assert.That(firstName, Is.Not.Empty); // PASS
Assert.That(contact.Keywords.Count, Is.GreaterThan(0)); // FAIL
}
}
I've also tried the following method:
[Test]
public void GetContacts_via_filter()
{
var uri = new TcmUri(101, 2, TcmItemTypes.StaticAddressBook);
var addressBook = new StaticAddressBook(uri);
var filter = new ContactFilter(UserContext.Current);
var contacts = Contact.GetContacts(filter, addressBook);
foreach (var contact in contacts)
{
var firstName = contact.ExtendedDetails["NAME"].StringValue;
Assert.That(contact.EmailAddress, Is.Not.Empty); // PASS
Assert.That(firstName, Is.Not.Empty); // PASS
Assert.That(contact.Keywords.Count, Is.GreaterThan(0)); // FAIL
}
}
I can even add a keyword to a Contact's Keywords collection and save it, and it appears correctly in Tridion, but when I retrieve the same contact again, the collection is once again empty.
Does anyone have any experience with this API, and/or know what the problem is?
This is because Keywords are not loaded when you get a list of Contacts. Only a subset of the data is available, for performance reasons.
To solve this, you will need to re-load each Contact. Since Contacts are streamed from the database, you cannot do this inside of your loop. So you'll want to build the list of Contacts first and then loop over them and load them in full.
For more info and examples, please see my blog post on the subject:
http://pkjaer.wordpress.com/2011/12/01/looping-through-contacts/

Categories

Resources