EFCore: Implementing a multi-environment DesignTimeDbContextFactory

Daniel Padua Ferreira
Daniel Padua Blog
4 min readJun 11, 2019

--

Intro

For those who already worked with Entity Framework Core using Code-First approach, knows that Migrations was a really good way to “version” the database structure of your project or service. Using CLI commands like: “dotnet ef migrations add application_v1” or “dotnet ef database update” , we can reflect the changes created from the class models to database, and deploy automatically through development, tests and production environments. However, when executing these commands in different environments, Entity Framework does not know in which database to connect to perform the updates unless you tell it explicitly. To solve this, the IDesignTimeDbContextFactory<YourDbContext> was created, and through it, it’s possible to configure it to search for different connection strings of your environments.

In this guide I’ll show an effective implementation of a generic DesignTimeDbContextFactory that supports development and production environments, and reads connection strings using the files appsettings.Development.json and appsettings.json. To simplify, I’ll use EF Core with SQLite, the whole code is available in this github repository.

Creating the project

Create a WebApi project named DesignTimeExample in the IDE/editor of your choice. I recommend you to use the following project structure:

Project Structure Example

Add a directory at project's root named: Data. We'll store in this folder all classes responsible for data access such as DbContext and our implementation of DesignTimeDbContextFactory. Also create a directory inside Data named: Migrations. This one will hold all snapshots of our database state. It's important to notice that despite these files are auto-generated, they define the state of the application, so it is necessary to version them.

Also add a folder named: Models. It will keep all classes related to business model or POCOs, in this example I'll use a really simple model of Plane and Flight.

Creating the Models

The following classes will be used as models to interact with the database tables:

Plane.cs
Flight.cs

Data Access

In this example we won't configure the names of the output tables for the sake of simplicity, so all tables will be named after the classes by default.

DbContext

To interact with the database we'll have to create a DbContext for the application, so create a file named: ApplicationDbContext.cs inside Data directory such as:

Connection String

Now we will configure a connection string in our development environment project configuration file. Edit appsettings.Development.json as it follows:

Development's appsettings

We'll pretend that this will be our development database, and the production will be in appsettings.json, like this:

Production's appsettings

Naturally in real scenario, the connection strings will point to different machines, so development and production are in separated infrastructures. But, in order to pretend this separation, the databases files generated for SQLite will be named: application_dev.db for development and application_prod.db for production.

DesignTimeDbContextFactory

To correctly identify the different connection strings when running EFCore's CLI commands, we'll need to implement IDesignTimeDbContextFactory interface. To do so, create a file named: DesignTimeDbContextFactory.cs in Data directory as it follows:

IDesignTimeDbContextFactory implementation

Explaining a bit of the code, we receive as parameters in constructor: connectionStringName and migrationAssemblyName. We also read the ASPNETCORE_ENVIRONMENT system's environment variable, which is the standard .NET Core variable for us to check in which environment we are in. We'll assume that if it contains with anything other than "Development", our implementation will assume the current environment is production. The files appsettings.Development.json and appsettings.json are added so it can read the input parameters and generate the migrations.

With this done, we'll only need to create a concrete implementation of this Factory, using the DbContext above. To do so, add the following class in ApplicationDbContext.cs:

Basically we only extended our DesignTimeDbContextFactory and passed the connection string name that is in the configuration files.

Creating and updating the migrations

With all this done, we can already test the migrations creation. Execute the following CLI command to create the initial version of our database structure:

dotnet ef migrations add application_v1 -o Data/Migrations

With the initial migration created, let's execute it in development environment. But before, check the content of the ASPNETCORE_ENVIRONMENT of your machine using:

Linux/OSx

echo $ASPNETCORE_ENVIRONMENT

Windows

echo %ASPNETCORE_ENVIRONMENT%

To set the variable content, use:

Linux/OSx

export ASPNETCORE_ENVIRONMENT=Development

Windows

set ASPNETCORE_ENVIRONMENT=Development

Run the CLI Command with your environment set to development:

dotnet ef database update

Notice that the file application_dev.db was generated at the root directory of your project. After that, set your environment to production and run again the command:

dotnet ef database update

Now the file application_prod.db was generated:

Conclusion

With this implementation it's easy to segregate environments in Design time (CLI commands executions), and it helps us to gain more control over the commands that are being executed by us or by a CI/CD software if you are running DevOps model.

See you soon!

If you found this post useful and would like to support me, you can do so by buying me a coffee using the button above!

Originally published at https://blog.danielpadua.dev on April 7, 2019.

--

--

Daniel Padua Ferreira
Daniel Padua Blog

Microsoft Certified Professional (MCP), Certified Tester Foundation Level (CTFL), Software Engineer, Technology and Cryptocurrencies enthusiast