With Entity Framework Core comes the great opportunity to improve upon EF’s legacy by incorporating all the lessons we’ve learned over the past few years of developing this software. For me, that means a chance to improve on one of the first features I worked on after joining the team over four years ago: Code First Migrations’ NuGet Commands. In this post, I want to talk about some of the improvements to these PowerShell commands.
Boxes and Lines
I want to start by talking about the design. Let’s examine the current implementation that has been used since version 4.3 and talk about the motivation behind it.
EF 4.3 Design
Let’s walk through it going from left to right. We start with the PowerShell module (
EntityFramework.psm1) which is
hosted in the main Visual Studio AppDomain. These call into the command classes which are contained in
EntityFramework.PowerShell.dll. Originally, this was all done inside the VS domain, but we found that since the
PowerShell assembly was being locked, the
EntityFramework NuGet package could not be uninstalled or updated. To solve
this, we delayed loading any assemblies until you invoked a command. We also loaded them inside a seperate domain so
that they could be unloaded later. Unfortunately, there were some things that couldn’t be done outside of the main VS
domain, and we had to add
EntityFramework.PowerShell.Utility.dll to perform cross-domain communication. So, we were
still locking an assembly, but it was done lazily. This worked pretty well until EF6 when we started calling PowerShell
commands when the NuGet package was installed. This took us right back to where we started with not being able to
uninstall or update. We needed a better design.
Let’s continue walking through the design. The command classes called into the
ToolingFacade class (contained in
EntityFramework.dll) which loaded the user’s application into another AppDomain. The purpose of this domain was to
mimic the runtime environment as much as possible so that when we invoked the DbContext and Migrations code, it would
behave as the user expected.
How has all of this been improved in EF Core?
EF Core Design
We still start in a PowerShell module (
EntityFramework.psm1). The PowerShell module is now responsible for creating
the runtime-like AppDomain for hosting the user’s application. It also takes care of doing things that need to be done
inside the VS domain. We still need cross-domain communication, but instead of loading the same types on both sides of
the boundary like we use to with
EntityFramework.PowerShell.Utility.dll, We’ve created a new class called
ForwardingProxy. I had to delve pretty deep into the dark world of .NET Remoting (my wound from the Morgul-blade still
aches from time to time), but I found a way of invoking methods on a type that isn’t loaded. The target type on the VS
side of the boundary is loaded from source using the
Add-Type cmdlet. The problem of locking assemblies was finally
The PowerShell module calls into the
Executor class which is a thin, cross-domain-friendly wrapper over the
MigrationTool class which invokes code inside the user’s application (via Reflection).
Shiny River Rocks
In addition to a new design, we’ve also made some small productivity enhancements to give the experience a nice polished feel.
When working on projects with multiple DbContext classes, you had to specify which
DbMigrationsConfiguration class to
use every time you invoked a command. This annoyed @lukewaters greatly. (He’s a tester on our team.) To make this
easier, we’ve enabled tab expansion of DbContext classes and migration names.
We went one step farther and added the
Use-DbContext command. This command allow you to specify a default DbContext to
use for the current PowerShell session. I like to call this the Luke command.
Use-DbContext UnicornContext Add-Migration TwentyPercentCooler
Less Is More
While playing around with the new commands, you may notice that a few commands and many parameters have been removed. We’ve made some design changes to Code First Migrations to enable this, and I’ll cover those in a future post. There is, however one change worth mentioning in this post.
Update-Database command has been split into two new commands.
Update-Databasejust applies migrations to the database
Script-Migrationgenerates a SQL script to apply the migrations to the database
EF Core is being built for new platforms including the Universal Windows Platform. I’m happy to say that, in addition to the Windows Desktop projects, the commands will also work with Universal Windows projects.