I’m learning about Entity Framework (the Beta 3 drop) by implementing an app to manage my collection of DVDs – in general I like Entity Framework and will continue to use it, but every once in a while, there’s something that makes me go ‘huh???!!!’ Here’s one such case.
Each DVD record can have multiple attribute records associated with it, which allows me to attach attributes to DVDs without having to have these attributes part of the base DVD tuple definition. This is primarily because many attributes, such as whether a DVD is a remake of another version of the movie don’t apply to all movies (I’ve thought of plotting the remake occurrance curve, i.e. I think that the decrease in risk tolerance/imagination is causing the remake frequency to rise, but thats another story)
So when I show information about a DVD, I have a few display fields that are connected to attributes, which may or may not be present. For example, I don’t associate tag lines with every movie, but if there’s one I think is a particular mot juste for a movie on IMDB, I’ll add it as an attribute. When the display populates some of the fixed fields that show attributes, it has to run a query that tells it if the specific attribute of interest is associated with the DVD that is being shown. This is primarily accomplished through a member function string GetTextAttribute(DVD theDVD, string theAttributeName), that will either return null (no attribute) or a string (the value of the attribute).
Within this function I use the IQueryable<T> mechanism, as shown in the following code fragment
DVDTextAttribute
ta;
ObjectQuery<DVDTextAttribute> attributes = _context.DVDTextAttribute;
IQueryable<DVDTextAttribute> attrQuery = from attribute in theDVD.DVDTextAttribute.CreateSourceQuery()
where attribute.AttributeName == keywordAttr
select attribute;
foreach(DVDTextAttribute attr in attrQuery)
blah blah blah
Unfortunately, this won’t work. When the foreach triggers the attempt to fetch the enumerator by calling GetEnumerator on attrQuery, this is what you’ll get…
Unable to create a constant value of type 'Closure type'. Only primitive types (for instance Int32, String and Guid) are supported in this context.
Now, if you modify the code slightly and do this
DVDTextAttribute ta;
ObjectQuery<DVDTextAttribute> attributes = _context.DVDTextAttribute;
IQueryable<DVDTextAttribute> attrQuery = from attribute in theDVD.DVDTextAttribute.CreateSourceQuery()
where attribute.AttributeName.ID == keywordAttr.ID
select attribute;
foreach(DVDTextAttribute attr in attrQuery)
The ID property is the primary key for the conceptual entity, so now Entity Framework is happy, I’m (sort of) happy, and life goes on. If you’ve just found this post because you’ve been searching for that error, you just have to be aware of the fact that Entity Factory isn’t completely matching the behavior of basic LINQ, you can’t do object comparisons as part of the query. Another person encountered this issue and seems to have reached the same conclusion that I have, their post is here.
Moving on to my gripe, it’s based on the fact that the Entity framework is knowledgably picky about the relational model of your database, and it’s pretty good about making sure that first, you actually have a decent relational model, and second, that you don’t undertake any action that would mess up this model, and it leverages the core LINQ architecture to accomplish its good works.
SO WHY IN THE NAME OF ALL THIS GOODNESS can’t they use what they already OBVIOUSLY KNOW and let you do object comparisons when you’re comparing apples to apples? It has enough information to know that the types being compared are identical, and it knows the primary key of the type. The basic LINQ architecture supports this, examples of it are scattered through the MSDN documentation, and that makes this behavior non-orthogonal at the very least!
Don’t get me wrong. Use the Entity Framework. It’s great. It just needs a few more things to be ‘adjusted’ before it goes golden, and the issue here is that when possible, it should support everything that basic LINQ does. It’s frustrating when you have a system that is capable of complaining at you about all the things you (really have) done wrong, but then it doesn’t exploit that same information to make your life easier. After all, if I changed the primary key in the DVDTextAttribute table, this code would now silently fail. That’s just not right…