Entity Framework 7: Updates and changes
- select the contributor at the end of the page -
In early 2015, I published a course titled "Looking Ahead to Entity Framework 7." This was created using an early version of the work that the EF team was doing on the total refresh of Entity Framework.
That version was referred to as Beta 4 although was still so malleable that the EF team even referred to it as an alpha. In fact, the only reason it had the tag “beta 4” was to align with the set of Nuget packages that were being released for ASP.NET 5 (now called ASP.NET Core) as it was developing. An important goal with EF7 was that it needed to work with ASP.NET 5, so the EF team needed to release their stable pre-releases on Nuget.org, which you can see here, in sync with ASP.NET 5. The nightly builds, which are available here, are a different story and are pushed frequently by the EF team whenever they are ready to do so.
Since the course was released some technical things have changed and even the release plans have changed. Rather than updating the course each time, potentially introducing more things that will change by RTM, I will wait until EF7/Core is closer to RTM and use that to create a course about the release.
In the meantime, I think it is important to have some awareness of the current landscape so that combined with watching Looking Ahead to Entity Framework 7, you can plan ahead effectively. My recommendation is to at least peruse this blog post before watching the course and then come back and read it more carefully when it will make more sense.
In this post I will address each of the 6 modules of the beta 4 course and alert you to what has changed and what to expect.
But first, the name has changed!
On January 19, the ASP.NET team announced that ASP.NET 5 was renamed to ASP.NET Core ,1.0 .NET Core 5 was renamed to .NET Core 1.0 and EF7 was renamed to Entity Framework Core 1.0 (aka EF Core 1.0). All of these are reset to version 1, creating a well-defined line between the previous versions and these new versions. Quoting Scott Hanselman’s blog post:
Why 1.0? Because these are new. The whole .NET Core concept is new. The .NET Core 1.0 CLI is very new. Not only that, but .NET Core isn't as complete as the full .NET Framework 4.6. We're still exploring server-side graphics libraries. We're still exploring gaps between ASP.NET 4.6 and ASP.NET Core 1.0.
This blog post was written before the name change and I’ve done my best to be clear about the naming, so don’t stress out about the few images that still say EF7. (Thanks!)
Module 1: Achieving Microsoft’s goals for EF with EF Core
The overarching goals for EF Core have not changed. Microsoft is wholly committed to a completely new code base for EF Core using modern software practices. The New Devices New Data Stores goal is still correct and, as mentioned in the course, the proof of concept work around NoSQL and Redis is still set aside.
While discussing where you can use (what was previously called) EF7, I showed a slide that talked about various frameworks.
This shifts a bit since the initial RTM of EF Core 1.0 will now support Universal Windows Platform (UWP), which targets Windows 10 devices including phones and tablets. Along with this change, the SQLite provider that had been set aside is now part of the initial RTM. Additionally, Windows Phone 8 and Windows Store support is still set aside for a later release after the initial RTM.
Here is an updated version of that slide I created for a recent conference:
Another change to the first module is related to the timeline. I explained how EF7 was going to focus on being able to release alongside of ASP.NET5 which meant setting aside the goals that they wanted to achieve. I also explained that when ASP.NET 5 was released, we would get a release of EF7 that would be still called a pre-release, not an RTM. Here is the slide I used while discussing this timeline:
These plans have changed somewhat. While the initial ASP.NET Core focused release of EF Core 1.0 is still going to be a subset of all that the team wants to implement in EF Core, it is now going to be a full release--an RTM, not a pre-release. So at the same time ASP.NET Core 1.0 is released, EF Core 1.0 will be released as well.
Here is what has not changed however:
- EF Core 1.0 will still be an “abbreviated” version.
- The features that the team is focusing on are those which align well with web applications. A good example of this is that they are working on improving the experience with disconnected entities and have set aside some magical relationship features such as explicit and lazy loading.
- EF Core 1.0 will not become the "official" version of Entity Framework.
- EF6 will continue to be the official version.
- EF6 will continue to be what's delivered when you install entityframework via Nuget.
- Your apps that use EF6 will not automatically update to EF Core 1.0!
- When installing the packages via Nuget, you won’t need to use “-pre” to distinguish between EF6 and EF Core.
EF6 will continue to install using install-package entityframework. There is no plain old entityframework package for EF Core. You would begin with your desired provider (e.g. entityframework.inmemory) and that will pull in all dependences e.g. entityframework.core and more. Since this is a full RTM for EF Core, you will no longer need to use the –pre.
Also, we now have a pretty well-delineated roadmap from the EF team on their github wiki. Most importantly is that the RC1 (Release Candidate with Go-Live) version of EF7 (as well as ASP.NET5) (prior to the name changes) was released in November 2015. According to the ASP.NET Core roadmap, there will be an RC2 in February with the RTM targeted to the first quarter of 2016 – apparently at the end of March to align with the Build conference. Another notable point is that you probably won’t see Visual Basic support until possibly Q3 of 2016.
According to the roadmap, EF Core will become the official version at some point in the future when the team feels that they have a critical mass of important ORM features implemented.
Here is an updated version of the above timeline slide which I used in my recent conference talk:
Module 2: Targeting EF7 initial and future releases
The list of things collected in what will be in the initial release has not changed significantly except that now, as mentioned above, EF7 will also support the UWP apps.
Re VS *& .NET versions. While ASP.NET 5 can run on the DNX environment, it can also be run on a full .NET Framework environment. In the course, it says this will be possible with 4.5 and beyond. Now it will be for .NET 4.5.1 and beyond. I don’t believe that the future releases will revert back to supporting 4.5 at all.
As mentioned above, the latest stable release is available on Nuget.org (today that is Beta 8). The nightly builds are available via the myget package source; the team is currently working on RC1 in the nightly builds.
In Beta4 there was still an EF7 package named EntityFramework. That no longer exists, enabling us to use that package name to specifically target EF6. For EF7, it’s easiest to begin with the provider that you want and then this will pull down all of the relevant dependencies. So for example:
install-package entityframework.sqlite” will pull down that package, the relational package (which includes migration support), the core package and others.
Don’t forget that the migrations commands are in a separate package: EntityFramework.Commands, which you need to explicitly install if you want to perform migrations in nuget or the DNX environment.
This is important: the “K Runtime” became dnx runtme for a short while and s now the .NET CoreUntil Beta 2, all of the commands that you run start with dnx now, not k.
dnx ef migrations add
After that, however, dnx will get replaced with “dotnet”.
Playing with the proof of concept features
The support for the stripped down framework used for Windows Phone 8 and Window Store apps was set aside and is still in that state. Those DLLs are not handily available. The tricks I used in the course to access those assemblies may or may not work still. I haven’t tested. Remember though that the SQLite assembly is now part of EF Core so that’s easy to get! And you can target Windows 10 UWA phone & tablet apps with EF Core.
Module 3: Querying and updating with EF7 (Disconnected Graphs change!)
Most of the changes from that evolved since I created this course with beta4 are around syntax. Even in the course you could see that at first the team was renaming methods to better suit how they wish they had been named. For example, rather than have DbSet.Add and DbSet.AddRange, they modified add to just take an overload that accepted a range. But just before I pushed the course live, they changed the methods back to Add taking a single object and AddRange specifically for a range. The reason for this is to lessen the blow of changes to the API. This could be something that would be backwards compatible with EF6.
There have been a number of changes like this as EF Core has evolved where some of the syntax was reverted to better align with EF6. Even behavior has shifted. Add is another example. EF6 and earlier had a pattern of [almost] always affecting full graphs when you pushed a root of a graph into the context with Add/attach/Delete or using the Entry().State property. And the effects were inconsistent. They experimented with completely separating behavior by making Add (etc) and Entry.State ONLY affect the root and then giving us a new method ChangeTracker.AddGraph solely for working with graphs. Since beta4, the team has narrowed in on a pattern that will be the final say on this matter. Here it is:
DbSet.Add, AddRange, Attach, AttachRange, Update, UpdateRange: These methods now take a new second parameter, which is an enum,GraphBehavior.
GraphBehavior values are IncludeDependents and SingleObject.
The default for the above methods is GraphBehavior.IncludeDependents, which means using it in a familiar way, e.g.:
This will result in almost familiar behavior. The catch is that the default behavior, IncludeDependents, is literally for dependent objects. It’s not going to include All Related Objects, and this distinction is important. Objects in a relationship are either principal (aka “parent”) or dependent (aka “child”). In the database these are easily distinguishable because the dependent is the one that has the foreign key back to the principal.
Consider the following Principal to Dependent relationships:
Order –> Line Items
It makes sense to create a new order, add some new line items to it and then add that order graph by calling context.Orders.Add(order). In this case, all of the dependent line items will be included in the operation (marked as Added, as well). It also makes sense to add an address record to a person and then add that person to the context; context.People.Add(person). Again, the dependent address would be marked as added. The category with its dependent products is also logical.
If you created a new line Address instance, and then identified its Person by setting someAddress.Person=somePerson, and then added that address via context.Addresses.Add(someAddress),EF7 would not mark the parent/principal object (somePerson) as Added. Adding a new address does not mean you’re adding the new person, so EF won’t make that presumption. I’m also a fan of building my model with aggregate roots that are in charge of the behavior of their related data. I wouldn’t allow my types to be used in such a way that the user of my API could create an address and randomly add it. So, this behavior aligns with coding patterns that I recommend.
It also solves another common problem in which devs use object instances to specify reference properties. I always recommend using foreign key properties for this, but I know that so many programmers have the following problem. Imagine this scenario: The form for building or editing the order has a place to enter a new shipping address for a customer. One of those fields is a dropdown for “region.” I query for a list of region objects and populate a dropdown list, as seen here:
I select Vermont and then my code does something like this: theOrder.theAddress.Region=(region)List.SelectedItem. I add the order with context.Orders.Add(theOrder) and call SaveChanges.
The next person who goes to add an address sees this:
We suddenly have two Vermonts! That’s because in EF6 (and earlier) everything in the graph is marked added, so the EF inserts the Vermont object into the database even though it was already there. In this case, the model sees REGION as the principal and ADDRESS as the dependent because ADDRESS has a foreign key back to REGION. EF Core will not include the principal; only the address will get inserted and the crops are saved!
Add, Attach, Update: Root Only
The default for Add, AddRange, Update, UpdateRange, Attach and AttachRange is to include dependents. Using the parameter, you can specify the SingleObject enum and then only the root entity of the graph will be affected by the method.
In Beta 8 this is the same as I explained in the course: If you specify an entity that’s in a graph, only that entity will be affected by setting the state. Any other objects that are part of the graph will be untouched.
ChangeTracker.AddGraph has changed to ChangeTracker.TrackGraph
The signature is the same; you pass in the graph and a lambda function. The lambda can express the state that you want the graph to use.
context.ChangeTracker.TrackGraph(someEntityWithRelatedObjectsAttached, e => e.Entry.State = EntityState.Added);
EF will walk the graph and apply that function to everything it discovers in the graph, skipping objects that are already being tracked (and their related objects). A cool feature of this method (same as when it was called AddGraph) is that that function does not have to express state—it can be any function you want.
The DbSet.Find method
This was set aside for post-RTM, but the team is reconsidering and may get it in for RTM. Learn more here.
Keep an eye on these methods as they will be shifting some more in Beta 2!
Module 4: Using and migrating relational databases
In this module I talked about some differences between how migrations worked, relative to how we’re used to them working in EF 4.3 to EF6. I also showed how migrations work in the k runtime commands if you’re using ASP.NET 5 and not able to use the familiar PowerShell commands. I also explained that the magic behavior of the DbInitializers, as well as automatic migrations, will not be part of EF7.
Not a lot has changed here. The team has streamlined the commands a bit more. It had introduced a new “apply-migration” command to replace update-database, but apply-migration is gone now from beta 8, you’ll just use the familiar update-database command.
As mentioned earlier, on the ASP.NET side, the commands are now dnx commands, not k commands and soon to be dotnet commands.So the current way to express adding a migration, therefore is:
dnx ef migrations add myAwesomeMigration
Not much else has changed from what is explained in Module 4.
There’s a nice chart in the July 23rd EF design meeting notes that shows the changes.
Not really migrating, but the commands now support reverse engineering with a scaffold command that has a bunch of helpful parameters to customize how the code is generated.
Module 5: EF7 futures
This module covers things that the team was exploring but set aside to focus on the ASP.NET 5-aligned release. Those were non-relational stores (with Redis and Azure Table Storage as proof of concept) and allowing EF to run on the trimmed down version of .NET that’s used for Windows 8 phone and tablet apps, which also relied on the new SQLite provider. Since the assemblies were still accessible, I showed a demo of a Windows Phone and Windows Store app that used SQLite, as well as one that used Azure Table Storage.
The Azure Table Storage and Redis providers are still set aside, as I explained in this module. And while it’s still true that EF Core will not initially support the Windows 8 Phone and Store apps when it’s released, the team did bring SQLite back into the fold for EF Core. SQLite was re-ignited because, as mentioned above, EF7 is now able to run on UWP, the Windows 10 platform that lets us create cross-device apps (which will also run on phones and tablets). These will benefit from using SQLite. In fact, I was able to rebuild the Cookie App from this module in UWP with EF7 RC1 and SQLite, and watch it run on emulators for phones and tablets.
Module 6: Interacting with the team
This module is about how EF7 is being developed openly on GitHub, and the story hasn’t changed. The team is eager to have developers try out EF7 and provide feedback in the form of issues or even Pull Requests. It continues to publish team meeting notes on the wiki, where we can converse further about these ideas.
The EF7 RoadMap
The roadmap did not exist when I created the course. It’s a great resource and I encourage you to check it out here.
That’s it for now, but you can bet that when EF 7.0.0 is officially here I’ll create a Getting Started course that will dig further into EF7 as a fully released framework.
A few notes about mappings
Since the course there were a few things I learned that either didn’t exist at the time or I just didn’t know about that I think are worth mentioning.
The pluralization support we’ve been used to in the past does not exist in EF Core. If you have a model with an entity named Person and another name Order, EF (& migrations) will presume that the relevant tables are also named Person and Order.
The next point will help you address the pluralization.
Custom Conventions that we got in EF6 are not in RC1 and won’t be in the first iteration of EF Core. They are targeted towards a future release--and should be in EF Core by the time it becomes the “official” version of EF.
So a custom convention such as this is not yet possible:
(EF6->) modelBuilder.Properties<String>().Configure(p => p.HasMaxLength(50))
However, Rowan Miller points out in his gist that we can continue to use a hack that we had before custom conventions existed to help (but not magically solve) with table name pluralization. And thanks to the new Name property introduced in C#6 (& VB but remember, we don’t have VB support yet) you could iterate through the entities and apply some rules. This gist of Rowan’s shows a simplistic “add s” rule:
foreach (var entity in modelBuilder.Model.GetEntityTypes())
modelBuilder.Entity(entity.Name).ToTable(entity.Name + "s");
Support for Table per Hierarchy (TPH) mappings was added as of RC1.
EF Core can now infer 1:1 mappings without you having to specify the principal and dependent. If it doesn’t get it right then you can use updated HasMany/HasOne fluent mappings. (These have been simplified!)