This morning I was reading a blog post by Neil Cowburn, of OpenNETCF fame, that talked about the lack of support for Linq in the Outlook object model for the .NET Compact Framework/Windows Mobile. Having played around with Linq a while ago I was surprised that there was an issue with running a query over the Outlook object model since in theory Linq queries can be run over any collection that implements IEnumerable. Neil’s code, as you can see from his post, is very similar to the following
OutlookSession outlook=new OutlookSession(); var nicks=from c in outlook.Contacts.Items where c.FirstName.StartsWith("Nick") orderby c.LastName select new{c.FirstName,c.LastName,c.Email1Address}; foreach (var n in nicks) { MessageBox.Show(n.FirstName); }
This code reports the error: “Could not find an implementation of the query pattern for source type ‘Microsoft.WindowsMobile.PocketOutlook.ContactCollection’. ‘Where’ not found. Consider explicitly specifying the type of the range variable ‘c’?” Following the directions the Linq statement can be changed to the following which explicitly defines the type of the range variable ‘c’:
var nicks=from Contact c in outlook.Contacts.Items ....
With this change the code compiles and executes as you would expect. Now lets look at the same thing in VB.NET:
Dim outlook As New OutlookSession Dim nicks = From c As Contact In outlook.Contacts.Items _ Where c.FirstName.StartsWith("Nick") _ Order By c.LastName _ Select c.FirstName, c.LastName, c.Email1Address For Each s In nicks MessageBox.Show(s.FirstName) Next
At first this seems ok, it compiles and executes as expected. However after making some changes to the project I realised that a compilation error was being thrown: “Option Strict On disallows implicit conversions from ‘Object’ to ‘Microsoft.WindowsMobile.PocketOutlook.Contact’.” Initially this seemed reasonable since one of the changes I had made was to set Option Strict to true (a MUST for all VB.NET developers). The problem here is that if I changed the type of ‘c’ to Object then I don’t get strong type checking for all property accessors I use in the remainder of the Linq statement, so what I need is a collection that returns Contacts instead of Objects. Looking at the definition of outlook.Contacts.Items, which is a ContactCollection, reveals that it only implements IEnumerable and not IEnumerable<Contact>, instead there is a GetEnumerator() which returns a ContactCollectionEnumerator. Unfortunately this doesn’t help me much and I had to manually casting each of the ‘c’ references.
So, the question I asked was why doesn’t the C# code complain about this type conversion? Well it appears that the C# implementation of Linq behaves slightly differently in that it filters the items in the collection in a similar way to the As operator does. If you were to manually iterate through the contacts you would use something like:
Contact c = unknownObject as Contact;
to make sure that c was either a Contact or null. You would then execute the Where clause followed by Selecting the relevant fields. Imho this is a much cleaner implementation that the VB.NET code, which again limits the developer, forcing them to write much more convoluted code.
Update: Thanks to fellow MVP and VB.NET Linq guru, Bill McCarthy, I am back on the straight and narrow. Changing the Linq statement to:
Dim nicks = From c In outlook.Contacts.Items.Cast(Of Contact)() _
shows similar behaviour to the C# code. You will notice that I’ve also dropped the “As Contact” which was in the previous code. As Option Infer is set to true the type of ‘c’ is automatically inferred based on the type of collection items. Bill pointed out that this explicit implementation of type filtering a collection is much better than the C# implementation where you might accidentally filter a collection – unexpectedly getting no elements returned because you were casting to the wrong type, instead of getting a compile error warning you about the type mismatch.