EF7 – v1 or v7?

A while ago we blogged about EF7 targeting new platforms and new data stores. In that post we shared that our EF6.x code base wasn’t setup to achieve what we wanted to in EF7, and that EF7 would be a “lightweight and extensible version of EF”.

That begs the question, is EF7 the next version of EF, or is it something new? Before we dig into the answer, let’s cover exactly what’s the same and what’s changing.

 

What’s staying the same?

When it comes to writing code, most of the top level experience is staying the same in EF7.

  • You still create a class that derives from DbContext and has DbSet<TEntity> properties for each type in your model.
  • You still use LINQ to write queries against your DbSet properties.
  • You still Add and Remove instances of types from your DbSet properties.
  • There are still DbContext.ChangeTracker and DbContext.Database properties for accessing change tracking information and database related APIs.

An example

For example, this code looks exactly the same in EF6.x and EF7.

 using (var db = new BloggingContext())
{
 db.Blogs.Add(new Blog { Url = "blogs.msdn.com/adonet" });
 db.SaveChanges();
 
 var blogs = from b in db.Blogs.Include(b => b.Posts)
             orderby b.Name
             select b;

 foreach (var blog in blogs)
 {
  Console.WriteLine(blog.Name);
  foreach (var post in blog.Posts)
  {
   Console.WriteLine(" -" + post.Title);
  }
 }
}

 

What’s changing?

While the top level API remains the same (or very similar), EF7 does also include a number of significant changes. These changes can be grouped into a series of buckets.

Bucket #1: New Features

One of the key motivations behind EF7 is to provide a code base that will allow us to more quickly add new features. While many of these will come after the initial RTM, we have been able to easily implement some of them as we build out the core framework.

Some examples of features already added to EF7 include:

  • Batching of updates for relational databases means that EF7 no longer sends an individual command for every insert/update/delete statement. In many situations EF7 will batch multiple statements together into a single roundtrip to the database. We’ll expand the capabilities of batching in future releases too.
  • Unique constrains allows you to identify additional unique keys within your entities in addition to the primary key. You can then use these alternate keys as the target of foreign key relationships.

 

Bucket #2: Behavior Changes

EF6 and earlier releases have some unintuitive behavior in the top level APIs. While the APIs are staying the same, we are taking the opportunity to remove some limitations and chose more expected behavior.

An example

An example of this is how queries are processed. In EF6.x the entire LINQ query was translated into a single SQL query that was executed in the database. This meant your query could only contain things that EF knew how to translate to SQL and you would often get complex SQL that did not perform well.

In EF7 we are adopting a model where the provider gets to select which bits of the query to execute in the database, and how they are executed. This means that query now supports evaluating parts of the query on the client rather than database. It also means the providers can make use of queries with multiple results sets etc., rather than creating one single SELECT with everything in it.

 

Bucket #3: Simple, lightweight components

Under the covers EF7 is built over the top of a lighter weight and more flexible set of components. Many of these provide the same functionality as components from EF6.x, but are designer to be faster, easier to use, and easier to replace or customize. To achieve this they are factored differently and bare varying resemblance to their counterparts from EF6.x.

An example

A good example of this is the metadata that EF stores about your entity types and how they map to the data store. The MetadataWorkspace from EF6.x (and earlier versions) was a complex component with a difficult API. MetadataWorkspace was not built with a lightweight and performant O/RM in mind and achieving basic tasks is difficult. For example here is the code to find out which table the Blog entity type is mapped to:

 using (var context = new BloggingContext())
{
 var metadata = ((IObjectContextAdapter)context).ObjectContext.MetadataWorkspace;
 var objectItemCollection = ((ObjectItemCollection)metadata.GetItemCollection(DataSpace.OSpace));
 
 var entityType = metadata
  .GetItems<EntityType>(DataSpace.OSpace)
  .Single(e => objectItemCollection.GetClrType(e) == typeof(Blog));
 
 var entitySet = metadata
  .GetItems<EntityContainer>(DataSpace.CSpace).Single()
  .EntitySets
  .Single(s => s.ElementType.Name == entityType.Name);
 
 var mapping = metadata.GetItems<EntityContainerMapping>(DataSpace.CSSpace).Single()
  .EntitySetMappings
  .Single(s => s.EntitySet == entitySet);
 
 var table = mapping
  .EntityTypeMappings.Single()
  .Fragments.Single()
  .StoreEntitySet;
 
 var tableName = (string)table.MetadataProperties["Table"].Value ?? table.Name;
}
 

In EF7 we are using a metadata model that is simple to use and purpose built for the needs of Entity Framework. To highlight this point, here is the EF7 code to achieve the same thing as the EF6.x code listed above.

 using (var db = new BloggingContext())
{
 var tableName = db.Model.GetEntityType(typeof(Blog)).Relational().Table;
} 

 

Bucket #4: Removal of some features

Removing features is always a tough decision, and not something we take lightly. Given the major changes in EF7 we have identified some features that we will not be bringing forward.

Most of the features not coming forwards in EF7 are legacy features that are only used by a very small number of developers.

  • Multiple Entity Sets per Type (MEST) is a legacy feature that allows you to use the same CLR type for multiple entity sets (i.e. you have a Products and RetiredProducts table that are both mapped to the Product class). This feature was never supported thru the DbContext API or code-based models. Although possible, it was difficult to use from the EF Designer too. Requirements like this are better solved with inheritance.
  • Very complex type to table mappings were possible in EF6.x. For example you could have an inheritance hierarchy that combined TPH, TPT, and TPC mappings as well as Entity Splitting all in the same hierarchy. This sounds great, but is one of the major contributing factors to the complexity of the MetadataWorkspace in EF6.x. In EF7 there will be cases where your CLR types need to more closely match your table structure.

Some of the features we are retiring because there is already another (we believe better) way of doing things. While we’d love to continue pulling everything forward, we need to balance time, resources, and the cost of adding support for highly requested features as we move forward. To be able to continue devloping and improving the stack we need to shed some of the baggage.

  • Retiring the EDMX model format in favor of code-based modeling is perhaps the most significant change in EF7. You can read more about this change and the reasoning behind it in our recent post on the topic.
  • ObjectContext API was the primary Entity Framework API until DbContext was introduced in EF4.1. Since then we have seen DbContext quickly become the API of choice for EF developers. Given this, and the much cleaner API surface that DbContext provides, we are not bringing ObjectContext forward into EF7. Of course, the important features you needed to drop down to ObjectContext API for in the past will be available from DbContext API, but factored into a cleaner API surface.

 

Not everything will be there in the initial release

Because much of the core of EF7 is new, the first release of EF7 isn’t going to have all the features that are required for all applications. There is always a tension between wanting to ship quickly and wanting to have more features in a given release. As soon as we have the core framework and basic functionality implemented we will provide a release of EF7 for folks to use in applications with simpler requirements. We’ll then provide a series of quick releases that add more and more features.

Of course, this means EF7 isn’t going to be usable for every application when it is first released, and for that reason we are continuing development of EF6.x for some time and expect many of our customers to remain on that release.

An example of this is lazy loading support, we know this is a critical feature for a number of developers, but at the same time there are many applications that can be developed without this feature. Rather than making everyone wait until it is implemented, we will ship when we have a stable code base and are confident that we have the correct factoring in our core components. To be clear, it's not that we are planning to remove lazy loading support from EF7, just that some apps can start taking advantage of the benefits of EF7 before lazy loading is implemented.

 

So, is it a v1 or a v7?

The answer is both. There were actually three options we discussed in terms of naming/branding for EF7:

  1. Call it v7 of Entity Framework – Given the top level API and patterns are the same as past releases, this is in many ways a major version of the same product. Per semantic versioning, breaking API changes and removal of features is a permissible (and inevitable) part of major releases.
  2. Create a sub-product under Entity Framework – This option was somewhat of a middle ground. While the developer experience is undoubtedly EF, creating a sub product would help communicate that there are also a significant number of changes. This would be akin to the “Entity Framework Everywhere” name we used for the initial design document we published in CodePlex.
  3. Call it something new and make it v1 – Given the number of changes, we did consider naming it something new.

We decided that once you start writing code, this feels so much like Entity Framework that is really isn’t something new (that ruled out option #3). While there are going to be some nuances between the v6 and v7 transition that need to be documented and explained, it would ultimately be more confusing to have two different frameworks that have almost identical APIs and patterns.

Options #1 and #2 both seem valid to us. Our ultimate conclusion was that #1 is going to cause some confusion in the short term, but make the most sense in the long term. To a lesser extent we’ve tackled similar hurdles in the past with the introduction of DbContext API and Code First in EF4.1 and then the move out of the .NET Framework in EF6 (and subsequent duplications of types, namespace changes, etc.). While these were confusing things to explain, in the long term it seems to have been the correct decision to continue with one product name. 

Of course, this is a somewhat subjective decision and there are no doubt folks who are going to agree and some who will disagree (there are even mixed opinions within our team).