July 2014

Volume 29 Number 7

Data Points : Tips for Updating and Refactoring Your Entity Framework Code, Part 2

Julie Lerman | July 2014

Julie LermanEntity Framework (EF) has been around for more than eight years and has gone through a lot of changes. This means developers are looking at applications that use early versions of EF to see how they can benefit from its newer enhancements. I’ve worked with a number of software teams going through this process and am sharing some lessons learned and guidance in a two-part series. Last month I discussed updating to EF6, using an old EF4 application as an example (bit.ly/1jqu5yQ). I also provided tips for breaking out smaller Entity Data Models from large models (EDMX or Code First), because I recommend working with multiple focused models rather than using one large model—laden with the complication of often unnecessary relationships—throughout your entire application.

This month, I’ll use a specific example to share more details about breaking out a small model, and then, using that small model, work through some of the problems you encounter when switching from the ObjectContext API to the newer DbContext API. 

For this article, I use an existing app that’s a bit more than the typical demo app in order to present the kind of real-world problems you might run into when refactoring your own solutions. It’s a small sample application from my book, “Programming Entity Framework, 2nd Edition” (O’Reilly Media, 2010), written using EF4 and the ObjectContext API. Its EDMX model was generated using Database First, then customized in the EF Designer. In this solution, I had shifted away from the default T4 code generation template that creates entity classes that all inherit from the EntityObject class. Instead, I used a T4 template that created plain old CLR objects (POCOs), which were supported beginning with EF4, and I had customized the template a bit. I should note that this is a client-side application and, therefore, doesn’t have some of the challenges of a disconnected app, where state tracking  is more challenging. Regardless, many of the issues you’ll see here apply to both scenarios.

In last month’s column, I upgraded this solution to use EF6 and the Microsoft .NET Framework 4.5, but I didn’t make any changes to my codebase. It’s still using the original T4 template to generate POCO classes and an ObjectContext class that manages the persistence.

The front end of the application uses Windows Presentation Foundation (WPF) for its UI. While the architecture is layered, my ideas on architecting applications have definitely evolved since then. Nevertheless, I’ll refrain from the full-blown refactor that would make my heart zing when I looked at the updated code.

My goals for this column are to:

  • Extract a smaller model that’s focused on one task.
  • Change the code generation of the smaller model to output the newer-style POCO classes and a DbContext class to manage the persistence.
  • Fix any existing code that uses the ObjectContext and broke because of the switch to DbContext. In many cases, this means creating duplicate methods and then modifying those to DbContext logic. This way, I won’t break the remaining code that’s still being used by the original model and its ObjectContext.
  • Look for opportunities to replace logic with simpler and more efficient functionality introduced in EF5 and EF6.

Creating the Trip Maintenance Model

In last month’s column, I provided pointers for identifying and extracting a smaller model from a large one. Following that guidance, I examined the model of this app, which isn’t very big, but does contain entities for managing a variety of tasks involved in running an adventure travel business. It has entities for maintaining trips defined by destinations, activities, accommodations,  dates, and other details, as well as customers, their reservations, payments, and sundry contact information. You can see the full model on the left side of Figure 1.

The Full Model on the Left, the Constrained Model on the Right, Created Using the Entities Shown in Red in the Bigger Model
Figure 1 The Full Model on the Left, the Constrained Model on the Right, Created Using the Entities Shown in Red in the Bigger Model

One of the application’s tasks allows users to define new trips as well as to maintain existing trips using a WPF form, as shown in Figure 2.

The Windows Presentation Foundation Form for Managing Trip Details
Figure 2 The Windows Presentation Foundation Form for Managing Trip Details

What the WPF form handles is the definition of trips: selecting a destination and a hotel, the start and end dates, and a variety of activities. Therefore, I can envision a model limited to satisfying just this set of tasks. I created a new model (also shown in Figure 1) using the Entity Data Model wizard and selected only the relevant tables. The model is also aware of the join table responsible for the many-to-many relationship between events and activities.

Next, I applied the same customizations (on which my code depends) to these entities in my new model that I had previously defined in the original model. Most of these were name changes to entities and their properties; for example, Event became Trip and Location became Destination. 

I’m working with an EDMX, but you can define a new model for Code First by creating a new DbContext class that targets only the four entities. In that case, however, you need to either define new types that don’t include the superfluous relationships or use the Fluent API mappings to ignore particular relationships. My January 2013 Data Points column, “Shrink Models with DDD Bounded Contexts” (bit.ly/1isIoGE), provides guidance for the Code First path. 

It’s nice to do away with the challenges of maintaining relationships about which, in this context, I just don’t care. For example, reservations, and all of the things tied to reservations (such as payments and customers), are now gone.

I also trimmed Lodging and Activity, because I really need their identity and name only for trip maintenance. Unfortunately, some rules around mapping to non-nullable database columns meant I had to retain CustomerID and DestinationID. But I no longer need navigation properties from Destination or Lodging back to Trip, so I deleted them from the model.

Next, I had to consider what to do with the code-generated classes. I already have classes generated from the other model, but those classes relate to relationships I don’t need (or have) in this model. Because I’ve been focused on Domain-Driven Design (DDD) for a number of years, I’m comfortable having a set of classes specific to this new model and using them separately from the other generated classes. These classes are in a separate project and a different namespace. Because they map back to a common database, I don’t have to worry about changes made in one place not showing up in another. Having classes that are focused just on the task of maintaining trips will make coding simpler, though it will mean some duplication across my solution. The redundancy is a trade-off I’m willing to make and one that I’ve come to terms with already in previous projects. The left side of Figure 3 shows the template (BreakAway.tt) and generated classes associated with the large model. They’re in their own project, BreakAwayEntities. I’ll explain the right side of Figure 3 shortly.

The Original Classes Generated from the T4 Template for the Large Model, Compared to the New Model and Its Generated Classes
Figure 3 The Original Classes Generated from the T4 Template for the Large Model, Compared to the New Model and Its Generated Classes

As per last month’s column, when I created the new model, I used my original code-generation template so that my only challenge during this step would be to make sure my application functions using the small model, without having to concurrently worry about changed EF APIs. I was able to do this by replacing the code inside the default template files (TripMaintenance.Context.tt and TripMaintenance.tt) with the code inside of my BreakAwayEntities.tt files. Additionally, I had to modify the file path in the template code to point to the new EDMX file.

In all, it took me about an hour of changing references and namespaces until I was able to run my little application and my tests again using the new model—not just retrieving data but editing and inserting new graphs of data.

Pulling the Plug: Moving to the DbContext API

Now I have a smaller model that’s much less complicated and will make the task of interacting with this data in my WPF code simpler. I’m ready to attack the harder chore: replacing my ObjectContext with the DbContext so I can delete code I wrote to make up for the complexity of working directly with the ObjectContext. I’m grateful I’ll be tangling only with the smaller surface area of code that’s related to my new model. It’s like re-breaking a broken arm so it will set properly, while the rest of the bones of my application will remain intact and pain-free.

Updating the Code Generation Template for my EDMX I’ll continue to use the EDMX, so in order to switch Trip Maintenance to the DbContext API, I must select a new code-generation template. I can easily access the one I want: EF 6.x DbContext Generator, because it was installed with Visual Studio 2013. First, I’ll delete the two TT files that are attached to the TripMaintenance.EDMX file, and then I can use the designer’s Add Code Generation Item tool to select the new template. (If you’re unfamiliar with using the code-generation templates, see the MSDN document, “EF Designer Code Generation Templates,” at bit.ly/1i7zU3Y).

Recall that I had customized the original template. A critical customization I’ll need to duplicate in the new template is removing the code that injects the virtual keyword on navigation properties. This will ensure lazy loading doesn’t get triggered, as my code depends on this behavior. If you’re using a T4 template and have customized anything for your original model, this is a very important step to keep in mind. (You can view an old video I created for MSDN that demonstrates editing an EDMX T4 template at bit.ly/1jKg4jB.)

One last detail was to attend to some partial classes I’d created for some of the entities and the ObjectContext. I copied the relevant ones into the new model project and made sure they were tied to the newly generated classes.

Fixing the AddObject, AttachObject and DeleteObject Methods Now it’s time to see the damage. While much of my broken code is particular to how I coded against the ObjectContext API, there’s a set of easily targeted methods that will be common to most applications, so I’ll hit that first.

The ObjectContext API offers multiple ways to add, attach and remove objects from an ObjectSet. For example, to add an item, such as an instance of trip named newTrip, you could use ObjectContext directly:

context.AddObject("Trips",newTrip)

Or you could do it from ObjectSet:

_context.Trips.AddObject(newTrip)

The ObjectContext method is a little clunky because it requires a string to identify to which set the entity belongs. The ObjectSet method is simpler because the set is already defined, but it still uses clunky terms: AddObject, AttachObject and DeleteObject. With DbContext, there’s just one way—through DbSet. The method names were simplified to Add, Attach and Remove to better emulate collection methods, for example:

_context.Trips.Add(newTrip);

Notice that DeleteObject became Remove. This can be confusing because while Remove aligns better with collections, it doesn’t really state the intent of the method, which is to eventually delete the record from the database. I’ve seen developers mistakenly assume that Collection.Remove will have the same result as DbSet.Remove—indicating to EF that the target entity be deleted from the database. But it won’t. So do be careful with that.

The rest of the problems I tackle are specific to how I used the ObjectContext in my original application. They aren’t necessarily the same problems you’ll encounter, but seeing these particular disruptions—and how to fix them—will help you prepare for whatever you encounter when making the switch to DbContext in your own solutions.

Fixing Code in the Context’s Custom Partial Classes I began my fix-up by building the project that contains my new model. Once this project is sorted out, I can attack projects that depend on it. The partial class I originally created to extend the ObjectContext was the first to fail.

One of the custom methods in the partial class is ManagedEntities, which helped me see what entities were being tracked by the context. ManagedEntities relied on an extension method I created: a parameterless overload of GetObjectStateEntries. My use of that overload was the cause of the compiler error:

public IEnumerable<T> ManagedEntities<T>() {
  var oses = ObjectStateManager.GetObjectStateEntries();
  return oses.Where(entry => entry.Entity is T)
             .Select(entry => (T)entry.Entity);
 }

Rather than fixing the underlying GetObjectStateEntries extension method, I can just eliminate it and the ManagedEntities method because the DbContext  API has a method on the DbSet class called Local I can use instead.

There are two approaches I could take to this refactor. One is to find all of the code using my new model that calls ManagedEntities and replace it with the DbSet.Local method. Here’s an example of code that uses ManagedEntities to iterate through all of the Trips the context is tracking:

foreach (var trip in _context.ManagedEntities<Trip>())

I could replace that with:

foreach (var trip in _context.Trips.Local.ToList())

Note that Local returns an ObservableCollection, so I add ToList to extract the entities from that.

Alternatively, if my code has many calls to ManagedEntities, I could just change the logic behind ManagedEntities and avoid having to edit all the places it’s used. Because the method is generic, it’s not as straightforward as simply using the Trips DbSet, but the change is still simple enough:

public IEnumerable<T> ManagedEntities<T>() where T : class  {
  return Set<T>().Local.ToList();
}

Most important, my Trip Management logic is no longer depending on the overloaded GetObjectStateEntries extension method. I can leave that method intact so that my original ObjectContext can continue to use it.

In the long run, many of the tricks I had to perform with extension methods became unnecessary when using the DbContext API. They were such commonly needed patterns that the DbContext API includes simple ways, like the Local method, to perform them.

The next broken method I found in the partial class was one I used to set an entity’s tracked state to Modified. Again, I had been forced to use some non-obvious EF code to do the deed. The line of code that fails is:

ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);

I can replace this with the more straightforward DbContext.Entry().State property. Because this code is in the partial class that extends my DbContext, I can access the Entry method directly rather than from an instance of TripMaintenanceContext. The new line of code is:

Entry(entity).State = EntityState.Modified;

The final method in my partial class—also broken—uses the ObjectSet.ApplyCurrentValues method to fix the state of a tracked entity using the values of another (untracked) instance of the same type. ApplyCurrentValues uses the identity value of the instance you pass in, finds the matching entity in the change tracker and then updates it using the values of the passed in object.

There’s no equivalent in DbContext to ApplyCurrentValues. Db­Context allows you to do a similar value replacement using Entry().CurrentValues().Set, but this requires that you already have access to the tracked entity. There’s no easy way to build a generic method to find that tracked entity to replace that functionality. However, all is not lost. You can just keep using the special ApplyCurrentValues method by leveraging the ability to access ObjectContext logic from a DbContext. Remember, DbContext is a wrapper around ObjectContext and the API provides a way to drill down to the underlying ObjectContext for special cases, such as the IObjectContextAdapter. I added a simple property, Core, to my partial class to make it easy to reuse:

public ObjectContext Core {
  get {
    return (this as IObjectContextAdapter).ObjectContext;
  }}

I then modified the relevant method of my partial class to continue to use ApplyCurrentValues, calling CreateObjectSet from the Core property. This gives me an ObjectSet so I can continue to use ApplyCurrentValues:

public void UpdateEntity<T>(T modifiedEntity) where T : class {
  var set = Core.CreateObjectSet<T>();
  set.ApplyCurrentValues(modifiedEntity);
}

With this last change, my model project was able to compile. Now it was time to deal with broken code in the layers between my UI and model.

MergeOption.NoTracking to AsNoTracking Queries, and Lambdas, Too Specifying that EF should not track results is an important way to avoid unnecessary processing and improve performance. There are multiple places in my app where I query for data that will be used only as a reference list. Here’s an example where I retrieve a list of read-only Trips along with their Destination information to be displayed in the ListBox on my UI. With the old API, you had to set the MergeOption on a query before executing it. Here’s the ugly code I had to write:

var query = _context.Trips.Include("Destination");
query.MergeOption = MergeOption.NoTracking;
_trips = query.OrderBy(t=>t.Destination.Name).ToList();

DbSet has a simpler way to do this using its AsNoTracking method. While I’m at it, I can get rid of the string in the Include method, as DbContext finally added the ability to use a lambda expression there. Here’s the revised code:

 

_trips= _context.Trips.AsNoTracking().Include(t=>t.Destination)
        .OrderBy(t => t.Destination.Name)
        .ToList();

DbSet.Local to the Rescue Again A number of places in my code required that I find out if an entity was being tracked when all I had was its identity value. I wrote a helper method, shown in Figure 4, to do this, and you can see why I wanted to encapsulate this code. Don’t bother trying to decipher it; it’s headed for the trash bucket.

Figure 4 My IsTracked Helper Method Became Unnecessary Thanks to the New DbSet.Local Method

public static bool IsTracked<TEntity>(this ObjectContext context,
  Expression<Func<TEntity, object>> keyProperty, int keyId)
  where TEntity : class
{
  var keyPropertyName =
    ((keyProperty.Body as UnaryExpression)
    .Operand as MemberExpression).Member.Name;
  var set = context.CreateObjectSet<TEntity>();
  var entitySetName = set.EntitySet.EntityContainer.Name + 
    "." + set.EntitySet.Name;
  var key = new EntityKey(entitySetName, keyPropertyName, keyId);
  ObjectStateEntry ose;
  if (context.ObjectStateManager.TryGetObjectStateEntry(key, out ose))
  {
    return true;
  }
  return false;
}

Here’s an example of code in my app that called IsTracked passing the value of a Trip I wanted to find:

_context.IsTracked<Trip>(t => t.TripID, tripId)

Thanks to the same DbSet.Local method I used earlier, I was able to replace that with:

_context.Trips.Local.Any(d => d.TripID == tripId))

And then I was able to delete the IsTracked method! Are you keeping track of how much code I’ve been able to delete so far?

Another method I had written for the application, AddActivity, needed to do more than just verify whether an entity was already being tracked. It needed to get that entity—another task Local can help me with. The AddActivity method (Figure 5) adds an Activity to a particular trip using ugly and non-obvious code that I had to write for the ObjectContext API. This involves a many-to-many relationship between Trip and Activity. Attaching an activity instance to a trip that’s being tracked causes EF to start tracking the activity, so I needed to protect the context from a duplicate—and my app from the resulting exception. In my method, I attempted to retrieve the entity’s ObjectStateEntry. The TryGetObjectStateEntry performs two tricks at once. First, it returns a Boolean if the entry is found and, second, returns either the found entry or null. If the entry wasn’t null, I used its entity to attach to the trip; otherwise, I attached the one passed into my AddActivity method. Just describing that is exhausting.

Figure 5 The Origial AddActivity Method Using the ObjectContext API

public void AddActivity(Activity activity)
{
  if (_context.GetEntityState(activity) == EntityState.Detached)
  {
    ObjectStateEntry existingOse;
    if (_context.ObjectStateManager
        .TryGetObjectStateEntry(activity, out existingOse))
    {
      activity = existingOse.Entity as Activity;
    }
    else     {
      _context.Activities.Attach(activity);
     }
  }
  _currentTrip.Activities.Add(activity);
}

I thought long and hard about an efficient way to perform this logic but ended up with code of about the same length. Remember, this is a many-to-many relationship and they do require more care. However, the code is easier to write and easier to read. You don’t have to tangle with the ObjectStateManager at all. Here’s the part of the method I updated to use a similar pattern as I did earlier with the Destination:

var trackedActivity=_context.Activities.Local
    .FirstOrDefault(a => a.ActivityID == activity.ActivityID);
if (trackedActivity != null) {
  activity = trackedActivity;
}
else {
  _context.Activities.Attach(activity);
}

Mission Complete

With this last fix, all of my tests passed and I was able to successfully use all of the features of the Trip Maintenance form. Now it’s time to look for opportunities to take advantage of new EF6 features.

More important, any further maintenance on this portion of my application will be simplified because many tasks that were difficult with the ObjectContext are so much easier with the DbContext API. Focusing on this small model, I’ve learned a lot that I can leverage for any other ObjectContext-to-DbContext transformations and I’m much better prepared for the future.

Equally important is to be smart about choosing what code should be updated and what should be left alone. I usually target features I know I’ll have to maintain in the future and don’t want to have to worry about interacting with the more difficult API. If you have code that will never be touched again and continues to work as EF evolves, I’d certainly think twice before diving into this challenge. Even if it’s for the best, a broken arm can be quite painful. 


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: Andrew Oakley