I recently hosted a live stream with badass-as-a-service Chris Klug, titled “Stop using Entity Framework Core as a DTO Provider!”. It’s worth a watch, and it gave me, a long-time Entity Framework user, a lot to think about and reevaluate in my workflows. That said, regardless of whether you agree with Chris’ style, he showed a masterclass of tool usage and an understanding that’s easy to admire. In our live stream, one new trick (to me, at least) stood out as something every Entity Framework Core user should know about.
In this post, we’ll explore one strategy to appease the dotnet ef
CLI tools regarding design-time configuration and
how it opens up a world of possibilities when dealing with database migrations.
Dependencies and ceremony
Entity Framework Core is heavily built around conventions and flexibility. It’s a multi-provider object-relational mapper (ORM), so it needs to operate under many unknown factors, with you, the developer, filling in the gaps. What database are you using? How many databases are you targeting? What migration strategies are you applying? The flexibility is excellent for solving complex problems but also leads developers into the woods of opaque exceptions.
One such problem you’ve likely encountered is the following error output when using the dotnet ef migrations add
command.
Unable to create a 'DbContext' of type ''. The exception
'Unable to resolve service for type
'Microsoft.EntityFrameworkCore.DbContextOptions`1[MigrationLibrary.Database]'
while attempting to activate 'MigrationLibrary.Database'.
The DbContextOptions
class is used to configure a DbContext
.
Everything looks good, but we need to see the OnConfiguring
implementation here, which adds the necessary answers to
which database we hope to use. Let’s tweak the code to solve this issue.
The error is gone now, but we’ve locked into options we can no longer configure outside the Database
class. We want
something else.
We still want to be able to pass in a DbContextOptions
instance. This allows us to configure the DbContext
to point
to different database implementations and connection strings and alter logging options. We want all that, trust me.
Let’s use a little-known feature to fix this: IDesignTimeDbContextFactory
.
IDesignTimeDbContextFactory to the rescue
The IDesignTimeDbContextFactory
is a tool that can change how you write migrations, and I don’t say this lightly.
Let’s look at the documentation.
Hmmm, nice. So what does that look like in C#?
Adding this class definition to your project lets Entity Framework’s design-time services answer some of the questions needed to generate migrations and other configuration options.
Running the dotnet ef migrations add
command now results in the following output.
Now, you can define all your migrations and the database configuration in one project while still allowing consuming projects to modify and change options within reason (i.e., you can’t apply SQLite migrations to a SQL Server database).
Conclusion
I love doing live streams because I get to learn from some of the best .NET professionals in the industry, and Chris Klug didn’t disappoint. Again, I highly recommend you watch the stream. This tip, amongst others, is sprinkled throughout the presentation. As always, thanks for reading and sharing these posts with friends and colleagues. Cheers.