Grab yourself a copy of Visual Studio code name "Orcas" and follow along as we create a program to access your data in Outlook.
Test Data
You will need to have some contacts in Outlook to test this program. Add at least 3 contacts, one with a home phone but no email, one with an email but no phone numbers, and one with no email and no phone numbers.
Getting Started
Start "Orcas" and click File->New->Project… Choose to create a Console Application in C# with the .NET Framework 3.5.
We need to add a reference to Outlook 2007. Right click on references in Solution Explorer and choose Add Reference…
Select the COM tab and choose the Microsoft Outlook 12.0 Object Library and press OK.
Two references will be added. Microsoft.Office.Core and Microsoft.Office.Interop.Outlook. Now add the following using statement to your Program.cs source file:
using Outlook = Microsoft.Office.Interop.Outlook;
We now need to get an instance of the Outlook Application object. Type the following code in the Main method:
Outlook._Application outlookObject = new Outlook.Application();
We want to look at the contacts stored in Outlook so we now get the contacts folder:
Outlook.MAPIFolder folder = outlookObject.ActiveExplorer().Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderContacts);
Now, let's define our LINQ query:
var contactsNoEmail = from contact in folder.Items
where contact.Email1Address == null
select contact;
This query is going to get the contacts from the contacts folder where the Email1Address is not defined. Now we need to iterate through our results:
foreach (Outlook.ContactItem contact in contactsNoEmail)
{
Console.WriteLine("{0} - {1} {2}", contact.LastFirstNoSpaceCompany, contact.FirstName, contact.LastName);
}
Lastly let's output that we have finished in case we get no results. Also we will request that the user press Enter so we can see our results when we run from the debugger:
Console.WriteLine("The end");
Console.ReadLine();
Now we need to build our project. Bo to the Build menu and choose Build Solution. The build should fail with the following error:
The type arguments for method 'System.Linq.Queryable.Where<TSource>(System.Linq.IQueryable<TSource>, System.Linq.Expressions.Expression<System.Linq.Func<TSource,bool>>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
To fix this we need to specify the type of the items in the folders Items collection. Change the first line of the LINQ query to:
var contactsNoEmail = from contact in folder.Items.OfType<Outlook.ContactItem>()
Now you should be able to build and run the program. When you run your program you may get a warning dialog. Choose to allow access if this is in response to your program running.
You should see a list of contacts who don't have an Email1Address defined followed by the string "The End". You will need to press Enter to end the program.
Adding a Second Query
Now, let's make our program a little more useful. It is possible that we have contacts with phone numbers but no email address. If we have a contact without either we want to know. We could do this in the same query, but for demonstration purposes we are going to do it as a second query. Add the following query after the first query inside your Main function:
var contactsNoPhone = from contact in folder.Items.OfType<Outlook.ContactItem>()
where (contact.HomeTelephoneNumber == null) && (contact.BusinessTelephoneNumber == null) && (contact.MobileTelephoneNumber == null)
select contact;
We check to see if the contact has a home, business or mobile number defined. If they don't we return that contact to the contactsNoPhone collection. To see the results of the second query we can change our iteration loop as follows:
foreach (Outlook.ContactItem contact in contactsNoPhone)
When we build and run the program we get a list of contacts that don't have a home, business or mobile number defined in the contact information.
The next step is to combine the two queries so that we can find contacts without a phone and without an email address. To do this we are going to intersect the two queries giving us only the contacts that are in both queries and changing the iteration to use the new variable:
var intersection = contactsNoEmail.Intersect(contactsNoPhone);
foreach (Outlook.ContactItem contact in intersection)
Build the program and run it. You will notice that you have no contacts listed even though you added some contacts that should match the query. The reason is that each query of Outlook returns new objects such that when they are compared, they appear different. This is due to there being a reference comparison rather than a value comparison. We can fix this with a couple of simple changes.
First we need to define a new class that implements IEqualityComparer for Outlook.ContactItem's. This class looks like this:
class ContactItemComparer : IEqualityComparer<Outlook.ContactItem>
{
public bool Equals(Outlook.ContactItem a, Outlook.ContactItem b)
{
return (a.LastFirstNoSpaceCompany == b.LastFirstNoSpaceCompany);
}
public int GetHashCode(Outlook.ContactItem a)
{
return a.Size;
}
}
This defines an Equals and a GetHashCode function. Note that these are by no means industrial strength implementations as they only compare the LastFirstNameNoSpaceCompany field and uses the size of the contact item as the hash. This will suffice for our example although checks for null will have to be done in production code. All that leaves is for us to specify the ContactItemComparer in the intersect function:
var intersection = contactsNoEmail.Intersect(contactsNoPhone, new ContactItemComparer());
When you now build and run your program you should see contacts that have no email in addition to no home, business or mobile phone number specified.
Summary
This example shows how easy it is to access the Outlook object model from LINQ. Although you could write this program without LINQ, the query syntax becomes compelling when you start to use it to access all your data stores.