Auto Access to non-Indexed Collections : Or How I Learned to Stop Worrying and Love the VB Compiler

I just finished a couple more How Do I videos on using EF with WPF using VS2008 SP1 (they’ll be published soon) and while I was translating my VB code to C# I stumbled upon an error in C# that does not happen in VB. This of course got me curious and so I thought I’d share what I found out.

I have an EDM defined with a parent entity Order and child OrderDetails that I created with the designer – very basic – like I show in this video. In the example I was working on, I was putting the results of an order query into a generic list but also including the OrderDetails so I end up with an EntityCollection called OrderDetails on every Order.

 OMSEntities db = new OMSEntities();

var results = from o in db.Orders.Include("OrderDetails")
              select o;

List<Order> orderList = new List<Order>(results);

However if I want to get the first Order’s first OrderDetail, I get an error in C#: “Cannot apply indexing with [] to an expression of type 'System.Data.Objects.DataClasses.EntityCollection<EFTestDAL.OrderDetail> '” when I write this line:

 //ERROR
Console.WriteLine(orderList[0].OrderDetails[0].OrderDetailID);

However, the VB code works perfectly.

 Dim db As New OMSEntities

Dim results = From o In db.Orders.Include("OrderDetails") _
              Select o

Dim orderList As New List(Of Order)(results)

'No Error
Console.WriteLine(orderList(0).OrderDetails(0).OrderDetailID)

So what gives in C#? At first I thought it was something with the way the EntityCollection was being generated in each of the designer code-behind for the languages, maybe I found a bug? So I created a single solution and used C# as the data access layer project and then added a C# client and a VB client project and still the same issue. VB worked fine against the C# defined EntityCollection. Hmmmm……

Next Stop, BING. I searched for the error message and no luck. Okay well it helps to actually read these error messages so let’s put on my glasses and break out my Microsoft Compiler Error Message Magic Translator* and yeah, as the title of this post suggests, the EntityCollection doesn’t actually implement an indexer. Okay so again, why does it work in VB?

The VB Compiler loves to help. It’s like uber-helpful (sometimes to its own fault, I admit), but this time it’s really helping in a good way. The way to get this to work in C# is to call the ElementAt or ElementAtOrDefault extension methods. So in C# to get the OrderDetail by index you write:

 //No ERROR
Console.WriteLine(orderList[0].OrderDetails.ElementAt(0).OrderDetailID);

So what Visual Basic is doing for you is exactly that. Actually it’s calling ElementAtOrDefault for you if the collection does not implement an indexer of its own. So you can write the same syntax for all collections. This help topic on the Extension Indexer Property gave us the clue we needed. So, if an IEnumerable or IQueryable doesn't define an indexer property, VB will translate the indexer syntax to the ElementAtOrDefault operator. Good to know!

So I asked Diego on the EF team why the EntityCollection doesn’t implement an indexer. I was told that the EF team decided long time ago that since there was conceptually no guarantee on the ordering of EntityCollections, they didn’t want to give the illusion that such ordering existed by exposing an indexer. The internal implementation of EntityCollection(Of T) uses HashSet(Of T), which is super-fast and doesn’t have an indexer.

Enjoy!

*No there is no such thing as a Microsoft Compiler Error Message Magic Translator that I know of so please don’t ask! But please feel free to create one on CodePlex. ;-)