Volume 27 Number 02
Data Points - A Few of My Favorite Things... in the Entity Framework 4.2 DbContext
By Julie Lerman | ebruary 2012
Even before Entity Framework 4.1 was released in early 2011, developers were focused on only half of what was given to us in that package: Code First. Code First lets you express your Entity Data Model using your domain classes and Code First configurations, a great alternative to developers who don’t want to use the visual designer to define the model. But every bit of sample code that you see for using Entity Framework (EF) with those classes and Code First-defined models is driven by another very important feature that came in EF 4.1: the DbContext class.
The ObjectContext class is part of the core EF API in the Microsoft .NET Framework 4 and is the class that allows you to perform queries, change tracking and update the database using the strongly typed classes that represent your model. The DbContext class is best described as a wrapper around ObjectContext that exposes the most commonly used features of ObjectContext as well as provides some simpler “shortcuts” to tasks that are frequently used but complicated to code directly with ObjectContext.
It’s my guidance and Microsoft’s that you should consider DbContext first when beginning new projects using EF. If you find that you occasionally need to access some of the more granular logic that the ObjectContext class provides, there’s a hook to get from a DbContext instance to its underlying ObjectContext:
var objectContext = (myDbContextInstance as IObjectContextAdapter).ObjectContext
If you know that you’ll be doing work that requires frequent use of ObjectContext features directly, you might prefer to use that rather than DbContext. But in general, the EF team recommends that you avoid using ObjectContext directly unless you’re prevented from using DbContext for some reason.
I’ll add the caveat that this guidance is meant for new projects. When working with the DbContext API, you get not only the new slimmer and smarter DbContext class, but also equally improved DbSet and DbQuery classes (counterparts to ObjectSet and ObjectQuery).
Although I'm a big fan of the DbContext, a few of its features have become my favorite little pets.
One of the new methods in the API is DbSet.Find. This helps with a common pattern developers use for data access: retrieving a single object based on its primary key.
With ObjectContext, you would have to create a full query and then execute the query using a LINQ method such as SingleOrDefault.
That would look like:
var partyHatQuery = from p in context.PartyHats where p.Id == 3 select p; var partyHatInstance = partyHatQuery.SingleOrDefault();
You could write that more efficiently with LINQ methods and a lambda:
var partyHatInstance = context.PartyHats.SingleOrDefault(p => p.Id == 3);
How often have you executed queries that perform this simple task? You might have even abstracted this code in your own simpler method.
This is just what the EF team did for you in the DbContext API. When working with DbContext, PartyHats would be a DbSet<PartyHat>, and you can use the DbSet.Find method to quickly achieve the same query execution with:
This method presumes the value you provide is the key value for the class you’re searching—in this case, PartyHat. EF will then execute a SingleOrDefault query on your behalf, searching for the data where Id is equal to the value passed in—in this case, 3. You’ll probably pass in a variable, not an actual value.
There’s another benefit to the DbSet.Find method that you can’t achieve with a query. The Find method will first look in memory for a matching object that’s being tracked by the context. If that’s found, then EF won’t bother querying the database. This is much more efficient than executing a query on the database only to throw away the results of the query if the object instance is already in memory—a wasted trip to the database that many developers trigger without realizing it.
You can also use DbSet.Find with composite keys. The signature of Find is not to take a single object but to take a parameter array. Therefore you can pass in a list of values to represent the values that make up the key.
When working with EF, I frequently found myself wanting to do something with objects that were already in memory and being tracked by a context. Typical places for this logic are in the SaveChanges override or SavingChanges method, where I’ll perform some validation. (Thanks to the new Validation API that’s available along with DbContext, I’ve been able to reduce much of this logic. But I won’t discuss Validation in this column.)
ObjectContext does provide a way to discover the objects that it’s tracking, but the API logic to do this is neither easy to find nor easy to code. In fact, in my book, “Programming Entity Framework” (O’Reilly Media, 2010), I wrote a set of four method extensions to help make this task simpler and more flexible.
More commonly, however, developers don’t realize the difference between executing a LINQ to Entities query on the context and interacting with those objects that the context is already tracking. For example, I’ve seen plenty of code where a developer retrieves data using a query and then attempts to perform logic on what’s now being managed by the query:
var balloons = context.Balloons.Where(b => b.Size == "L").ToList(); var balloonCount = context.Balloons.Count();
In fact, these are two separate queries. The second line of code executes another query on the database and returns a count of all balloons. Typically, what the developer had intended was to get a count of the results—that is, balloons.Count.
If you don’t have access to a variable but still want to find out how many Balloon objects an ObjectContext is tracking, there’s a way to find out, but it’s not easy: ObjectContext exposes an ObjectStateManager, which has a method called GetObjectStateEntries. This method requires that you pass in one or more EntityState enums (for example, Added, Modifed and so on) so it knows which entries to return. Although the results are queryable, filtering is unwieldy and even then what it returns is not your entities, but the ObjectStateEntry instances that represent state information about your objects.
This means that without the use of my extension methods, code to help get the count of the balloons in memory looks like this:
objectContext.ObjectStateManager .GetObjectStateEntries(EntityState.Added | EntityState.Modified | EntityState.Unchanged) .Where(e => e.Entity is Balloon).Count();
If you want to capture those Balloon objects, not just the ObjectStateEntry instances, then you have to add some casting to return the ObjectStateEntry.Entity types as Balloons:
objectContext.ObjectStateManager .GetObjectStateEntries(EntityState.Added | EntityState.Modified | EntityState.Unchanged) .Where(e => e.Entity is Balloon) .Select(e => e.Entity as Balloon);
Seeing this code might make you appreciate the new property DbSet.Local almost as much as I do.
Using DbSet.Local to get all of the tracked Balloon instances from the context, you can simply call:
“Local” returns an ObservableCollection that provides two benefits. The first is that it’s queryable, so you can return whatever subset of the locally cached Balloons you want. The second is that your code (or components such as data-binding controls) can listen for and react to objects being added to or removed from the cache.
Besides the discoverable property and the reduced code, there are two other notable differences between using DbSet.Local and GetObjectStateEntries. One is that Local returns objects from the particular DbSet only, whereas GetObjectStateEntries returns entries regardless of the type of objects they represent. The other difference is that Local won’t return objects that the context knows are marked as Deleted. With GetObjectStateEntries, you have access to Added, Modified, Unchanged and Deleted objects as specified in the parameter list that you provide to the method.
NoTracking LINQ Queries
When discussing performance with clients, I often recommend they take advantage of the EF ability to return data that doesn’t need to be tracked by the context. For example, you may have data you need to supply for a drop-down selection list. You’ll never need to make changes to that data, much less persist it to the database. Therefore, it’s smart to avoid the performance hit taken when EF creates ObjectStateEntry instances for each object it’s tracking, as well as forcing the context to be aware of any changes made to those objects.
But with ObjectContext, the NoTracking support is available only through the ObjectQuery class, not from LINQ to Entities queries.
Here’s a typical example of getting a NoTracking query using an ObjectContext (called context):
string entitySQL = " SELECT p, p.Filling " + "FROM PartyContext.Pinatas AS p " + "WHERE p.Filling.Description='Candy'"; var query=context.CreateQuery<DbDataRecord>(entitySQL); query.MergeOption = System.Data.Objects.MergeOption.NoTracking; var pinatasWithFilling=query.ToList();
The retrieved piñatas and fillings would be objects in memory, but the context would have no knowledge of them.
However, if you were to use the following LINQ to Entities query, which returns an IQueryable, not an ObjectQuery, there would be no MergeOption property:
One solution is to cast the LINQ query to an ObjectQuery and then set the MergeOption. This is not only not obvious but also clunky.
Recognizing this, the EF team found a way to let you have your party cake and eat it, too, with the new AsNoTracking extension method for IQueryables that’s part of the DbContext API. Now I can tack it on to my LINQ query:
context.Pinatas.Include("Filling") .Where(p=>p.Filling.Description=="Candy") .AsNoTracking();
This will return a set of Pinatas and Fillings that will be ignored by the context. EF won’t wastethe effort of instantiating DbEntityEntry objects (the DbContext API version of ObjectStateEntry) for each object. Nor will it waste the effort of forcing the context to inspect those objects when DetectChanges is called.
It’s simple to code and very discoverable through IntelliSense.
Icing on the Cake
These three features—Find, Local and AsNoTracking—don’t enable me to perform tasks that weren’t achievable with the ObjectContext. But they do make me happy every time I use them. There are so many coding tasks that the DbContext API simplifies (compared to using the ObjectContext) that it has streamlined my application development quite a bit. I’ve also returned to old ObjectContext code and refactored it to use DbContext along with Code First and have been able to significantly reduce the amount of code in those apps. But for developers who aren’t as intimately familiar with the EF as I am, the discoverability of so many of its capabilities will make a big difference for getting up and running with it.
Julie Lerman is a Microsoft MVP, .NET mentor and consultant who lives in the hills of Vermont. You can find her presenting on data access and other Microsoft .NET topics at user groups and conferences around the world. She blogs at thedatafarm.com/blog and is the author of “Programming Entity Framework” (2010) and “Programming Entity Framework: Code First” (2011), both from O’Reilly Media. Follow her on Twitter at twitter.com/julielerman.
Thanks to the following technical expert for reviewing this article: Arthur Vickers