Volume 29 Number 5
Data Points : Tips for Updating and Refactoring Your Entity Framework Code
Julie Lerman | May 2014
I have worked with many clients to help refactor existing software that uses Entity Framework (EF). In the case of EF code, refactoring can have a variety of meanings and often involve an update. In this column, I’ll take a look at some of the ways you might be overhauling your EF code, or applications that involve EF. For each of these approaches, I’ll provide some guidance—based on my experiences working with clients’ production applications—that should help you be well-prepared and avoid some headaches.
The changes I’ll discuss are:
- Updating to a new version of Entity Framework
- Breaking up a large Entity Data Model
- Replacing the ObjectContext API with the DbContext API
I’ll cover the first two at a high level this month and then follow up in my next column by digging into the last scenario with guidance, as well as some concrete examples.
Before you embark on any of these tasks, there’s an initial bit of advice I highly recommend you follow: Do them one at a time. I’ve taken part in endeavors that include an old project that uses EF4, a huge model and the ObjectContext API. Attempting to change all three at the same time can only lead to tears and frustration—and, perhaps, worse. In this case, my suggested course was to first update the EF version without making any other changes, and then make sure everything continued to work. The next step involved identifying an area of the model that could be extracted into a new small model. But, initially, I let the new model continue to target the ObjectContext API. When everything was back in working order, the shift to the DbContext API was started, but for that small model only. Otherwise, too many things could break throughout the application and you’d be on a wild, incoherent mission to hunt down a variety of bugs. By shifting one small model at a time, you have a smaller surface area of broken code to rework and you can learn some good lessons and patterns that will make shifting the next small model much less painful.
Updating to a Newer Version of Entity Framework
Thanks to the EF team’s focus on backward compatibility, moving from one version to another provides minimal friction. I’ll focus on updating to EF6 —the major version of EF6, as well as its minor updates, such as EF6.02 and EF6.1.
The worst issues with moving from EF4, EF4.1 or EF5 to EF6 (which, in my opinion, really aren’t so bad) result from some namespace changes. Because the original APIs are still in the Microsoft .NET Framework, having them duplicated in EF6 would cause a problem. So in the EF6 API, those classes are in a namespace that’s different from System.Data to avoid conflicts. For example, there are a number of namespaces in the .NET-based EF APIs that begin with System.Data.Objects, System.Data.Common, System.Data.Mapping, System.Data.Query, as well as a few others. There are also some classes and enums that live directly in System.Data; for example, System.Data.EntityException and System.Data.EntityState. Most of the classes and namespaces that were tied directly to System.Data in this way were moved to the new namespace root, System.Data.Entity.Core. There are a handful of classes moved into System.Data.Entity, such as EntityState, which is now found at System.Data.Entity.EntityState. For example, the Mapping namespace is now System.Data.Entity.Core.Mapping while the Objects namespace is now System.Data.Entity.Core.Objects. See item 4 in the Data Developer Center documentation, “Upgrading to EF6” (bit.ly/OtrKvA), for the specific exceptions that didn’t go into System.Data.Entity.Core.
When updating existing applications to EF6, I just let the compiler highlight the changes by showing me any “The type or namespace … is not found” errors and then I do some solution-wide finding and replacing to correct the namespaces.
As an example, I started with a small sample solution from the second edition of my book, “Programming Entity Framework.” This solution was written using EF4, an EDMX model, code-generated POCO entity classes and the ObjectContext API. Code First and the DbContext API didn’t exist at the time. Before I got started, I verified the application still worked (debugging in Visual Studio 2013).
Though I’m focusing on a bigger leap—from EF4 to EF6—you need to follow the same path of namespace fixes if you’re going from EF5 to EF6, because these namespace changes occurred between EF5 and EF6. Moving from EF4 directly to EF6 has a few extra challenges, plus my old solution used a T4 template that doesn’t have a direct replacement.
My Steps to Update from EF4 to EF6
If you’re used to getting Entity Framework using the NuGet Package distribution, you’ll need to think back to a time when EF was simply part of the .NET Framework and all of its DLLs lived in the Windows Global Assembly Cache (GAC). Before updating to EF6, I manually removed the references to System.Data.Entity (version 188.8.131.52) in each of my solution’s projects. I also cleaned the solution (by right-clicking the solution in Solution Explorer and choosing Clean Solution) to be sure any of the original DLLs I may have forced into the BIN folders were gone. It turns out my diligence wasn’t necessary because the NuGet package installer for EF6 removes the old references for you.
Then I used NuGet to install EF6 into the relevant projects and rebuilt the solution. The project dependencies in your own solutions will drive how quickly the namespace issues surface. With my solution, I was only shown one namespace error at first. I fixed that and rebuilt the solution, and then saw many more errors—all but one were namespace issues. For the one exception, the compiler provided a helpful message telling me that a less frequently used attribute I had taken advantage of (EdmFunction) had undergone a name change (and was therefore marked as Obsolete) and had been replaced by the attribute DbFunction.
After a series of iterative namespace fixes and rebuilds, which took only a few minutes for this small solution, I was able to successfully build and run the application—viewing, editing and saving data.
Fixing the ObjectContext Plus POCO T4 Template
There’s one other possible task to keep in mind. My solution used an EDMX—an Entity Data Model designed and maintained with the EF Designer. Because I created it in Visual Studio 2010 with EF4, it relied on an older code-generation template that generated an ObjectContext to manage all of the data persistence and caching. That template generated POCO classes (which have no dependency on Entity Framework) from the entities in the model. If I make any changes to my model, I’ll need to regenerate the classes and the context. But that older template—the one that generates the context—isn’t aware of the new namespaces. While there are DbContext templates and ObjectContext (plus EntityObjects) templates (see Figure 1), there’s no replacement for my template that provides the ObjectContext plus POCOs combination. And to make things more interesting, I had customized the template I used. So rather than selecting a new template that wouldn’t work with my application, I made two small changes to the Context.tt template that was stored in my existing solution:
- On Line 42, “using System.Data.Objects;” becomes “using System.Data.Entity.Core.Objects;”
- On line 43, “using System.Data.EntityClient;” becomes “using System.Data.Entity.Core.EntityClient;”
Figure 1 You’ll Find Templates to Generate DbContext Plus POCOs or ObjectContext Plus Non-POCOs.
Now, any time I regenerate the classes from the model, the ObjectContext class will get the correct namespaces and my custom POCO-generated classes will continue to function in my application. Note that the Data Developer Center documentation I mentioned earlier explains how to use the supported templates.
Benefits You’ll Gain Without Changing Any More Code
I found updating the application to use EF6 pretty painless. However, there’s a very important point to consider. While the application is now using the most recent version of Entity Framework, it’s benefitting only from the underlying improvements to Entity Framework—in particular some great performance gains that came in EF5 and EF6. Because the bulk of these performance gains came in EF5, moving from EF5 to EF6 without leveraging the other new features won’t have as much impact. To see what else is new in EF6, check out my December 2013 article, “Entity Framework 6: The Ninja Edition” (bit.ly/1qJgwlf). Many of the improvements are related features of the DbContext API and Code First. If you want to update to EF6 and also update to DbContext, I recommend starting with the simple upgrade to EF6, and getting everything working again before you begin to shift to the DbContext API. That’s a more complex change that I’ll cover in detail in my next column.
Even with these possibly minimal changes, your codebase is now ready to leverage the more modern APIs.
Breaking Up a Large Model
Whether you’ve used the EF Designer to create your model or the Code First workflow (see “Demystifying Entity Framework Strategies: Model Creation Workflow” at bit.ly/Ou399J), models with many entities in them can cause design-time and even runtime problems. My personal experience is that large models are just too unwieldy and difficult to maintain. If you’re using the Designer and have many entities (hundreds), not only does it take longer for the designer to open and display the model, it’s difficult to visually navigate the model. Thankfully, the Designer gained some great capabilities in Visual Studio 2012 that help (see “Entity Framework Designer Gets Some Love in Visual Studio 2012” at bit.ly/1kV4vZ8).
Even so, I always recommend that models be smaller. In my column, “Shrink EF Models with DDD Bounded Contexts” (bit.ly/1isIoGE), I talk about the benefits of smaller models, as well as some strategies for using them in your application. If you already have a big model, however, it can be a challenge to break that apart. I’ve had clients who were working with models they reverse-engineered from huge databases ending up with 700 or even 1,000 entities. Whether you’re shrinking an EDMX model in the Designer or using Code First, the story is the same: Breaking up is hard to do.
Here are some useful pointers for breaking up a large model into smaller models for simpler maintenance and potentially better runtime performance.
Don’t attempt to refactor the entire model at once. For each small model you extract, you’ll need to do some additional code refactoring because references change and you might have some relationship code to tangle with as well.
So the first step is to identify a section of the model that’s nearly autonomous. Don’t worry about overlap at first. For example, you might be working on a system for a company that manufactures and sells products. The software may include a feature for maintaining your sales force—personnel data, contact information, sales territory and so forth. Another part of the software might reference those sales people when building a client’s order based on the definition of your sales territories. And yet another part might be tracking their sales commissions. So tackle one particular scenario—say, maintaining the salesperson list—at a time.
Here are the steps for shifting to smaller models:
- Identify the entities involved in that scenario (see Figure 2).
- Create a completely new project.
- In that project, define a new model (either with the EF Designer or using Code First) that’s aware of the relevant entities.
- Identify the application code involved with product maintenance.
- Update the relevant code that uses the original context (for querying, saving changes or other functionality) so it uses the new context you’ve created.
- Refactor the existing code until the target functionality is working. If you have automated testing in place, this might make the task of refactoring more efficient.
Some additional advice to keep in mind during this process:
- Do not remove these entities from the big model. Doing so would cause a lot of things to break. Just leave them there and forget about them for now.
- Keep a log of what kind of refactoring you had to do to get the application to work with the new smaller model.
Figure 2 SalesPerson and Territory Maintenance Can Be Extracted into a Separate Model with Little Impact to Other Entities
You’ll learn patterns you can apply as you continue to break off other focused models from the big model.
By iteratively adding one new small model at a time to your solution and directing code to use it, your experience will be much more pleasant. In the big model, these same entities might be tangled up in relationships that have nothing to do with the SalesPerson maintenance task. For example, you might have a relationship from SalesPerson to a Sales Order, so you probably won’t want to remove SalesPerson completely from the model. It’s simpler to have a trimmed down, read-only SalesPerson type used as a reference during Order creation in one model and then the full-blown editable SalesPerson type (with no knowledge of Orders) used for maintenance in another model. When all is done, both entities can continue pointing to the same database. If you’re using Code First and migrations, check the aforementioned article on shrinking EF models for guidance on sharing a database when you have multiple overlapping models.
Eventually you could end up with many small models and still some references to the big model. At that point, it will be easier to identify which entities are no longer touched in the big model and can be safely removed.
Patience: Yes, It’s a Virtue
The most important takeaway is to approach these updates and refactors in small bites and at each iteration, get your tests and application functioning again before moving on. Consider updating to a newer version of Entity Framework as its own work item. And even that can be broken in two as you first concentrate on pulling in the newer APIs and making sure your code continues to run before changing the code to take advantage of new features. The same baby steps apply to breaking up a big model—especially if you’re planning to update from ObjectContext to DbContext. Extract small models and refactor to make sure relevant logic can function with the new smaller model. Once that’s working, it’s time to break your ties to ObjectContext, which will break a bunch of code at first. But at least that broken code will be quarantined to a smaller area of your codebase and you’ll be refactoring less code at a time.
In my next column, I’ll delve into the more painful but totally achievable goal of moving ObjectContext code to use the DbContext API.
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.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) as well as a Code First edition (2011) and a DbContext edition (2012), all from O’Reilly Media. Follow her on Twitter at twitter.com/julielerman and see her Pluralsight courses at juliel.me/PS-Videos.
Thanks to the following Microsoft technical expert for reviewing this article: Rowan Miller