EF Core 6 new features and changes for .NET 6

Published: Sunday 12 December 2021

EF Core 6 is upon us, and unless you have been living under a rock, you would know that .NET 6 was launched last month, and there are a number of changes were implemented to Entity Framework Core as part of the update. And we are going to have a look at some of them.

First, we will have a look at migration bundles. The migration bundle is an executable that contains everything to run a migration. It can be used as part of continuous deployment (CD) where it can be integrated as a deployment step in a DevOps pipeline.

Afterwards, we have a look at Entity Framework's support for temporal tables in SQL Server. Temporal tables are used to keep a full history of changes in a table, and these can now be integrated with EF Core.

Finally, we will have a look at pre-convention model configuration and how to set that up in EF Core. Gone are the days where we have to specify properties for common data types.

Feature #1 - Migration bundles

Entity Framework's migrations have been a familiar feature for code-first integration. As new features get implemented into an application, it's possible that new entities and properties are added to support these. Therefore, the database schema needs to kept in sync with the application.

This is where migrations come in. EF Core's migrations feature allows us to update the database schema and keep it in sync with the application's data model.

Migrations are generated in a class, and they contain the database schemas to update. We then run a PowerShell script to execute the script. This will either apply or revert the changes to the SQL Server database.

With EF Core 6, we can now create these migration scripts as an executable. This executable can be run as part of a deployment step in the DevOps pipeline, and we are going to go through the steps to set this up.

The first step is to make sure that we have migrations set up. We are assuming that you already have set up migrations in EF Core before. If you haven't, head over to Microsoft's documentation that talks about migrations.

With our migrations set up, we now need to bundle them into an executable. In Visual Studio 2022, we can go to Tools > NuGet Package Manager > Package Manager Console, and run the following script:

Bundle-Migration

An unexpected error

However, when we ran this script, we are greeted with the following error:

Build failed. Use --verbose to see errors.

So we ran it with the -verbose parameter to see if that helped us.

Bundle-Migration -verbose

We had a slightly more descriptive error, but still not very helpful.

The process cannot access the file 'RoundTheCode.EFCore.dll' because it is being used by another process.  [RoundTheCode.EFCore.csproj]

Afterwards, we tried to run it in Powershell (in admin mode) using the dotnet command line:

dotnet ef migrations bundle --verbose

A resolution

Again, we were greeted with the same errors as above. The way we managed to get around it was to append the --configuration parameter to the dotnet command line script, and ensured that it contained anything apart from debug or release.

dotnet ef migrations bundle --verbose --configuration abc

This worked for us, and generated a efbundles.exe file. We can then run that file and it would update our database schema with the entities and properties within our application.

Please get in contact with us if you know why we are getting this error and how to resolve it.

Feature #2 - Temporal tables

Temporal tables were introduced in SQL Server 2016. These tables have the ability to keep a full history of changes within a table. This means that if we need to know what data was stored at any point in time, we can go ahead and query it.

EF Core 6 supports the ability for tables to be set up as temporal tables, and we are going to go through the steps to set it up.

In our DbContext, we have set up a Player and Team entity, and we are now going to go ahead and make them temporal. We can do that by overriding the OnModelCreating method and use the IsTemporal method:

public class EfCoreDbContext : DbContext
{
	...

	protected override void OnModelCreating(ModelBuilder modelBuilder)
	{
		modelBuilder.Entity<Player>()
		.ToTable(action =>
		{
			action.IsTemporal();
		});

		modelBuilder.Entity<Team>()
		.ToTable(action =>
		{
			action.IsTemporal();
		});
	}
}

Now that we've made that change, we need to add a migration for it. In Visual Studio 2022, we can go to Tools > NuGet Package Manager > Package Manager Console, and run the following script:

Add-Migration Temporal

Assuming we are not using migration bundles, we need to update the database and we can run the following script in Package Manager Console to do that:

Update-Database

Now that we've set up temporal tables, we can query the history of changes. We can do that by running a query similar to the following:

[ApiController]
public class TestController : ControllerBase
{
	...

	[HttpGet("sample")]
	public virtual async Task<ActionResult<List<Team>>> GetSample()
	{
		var teams = await _dbContext.Teams.TemporalAll().Where(x => x.Id == 1).ToListAsync();

		return Ok(teams);
	}
}

The TemporalAll operation retrieves the full history of changes for that particular table. If we wish to be date specific, we can use the TemporalAsOf operation, passing in the UTC date as a parameter. There is also a TemporalBetweenTemporalContainedIn and TemporalFromTo operation that we can use.

Feature #3 - Pre-convention model configuration

Regular users of EF Core will be familiar with having to set a max length for each string in an entity. Otherwise, each string would be migrated as a NVARCHAR(max) column type in SQL Server. But with EF Core 6, we can set a convention for this so we don't have to set it for each individual property.

The way we do this is very simple. We can override the ConfigureConventions method in our DbContext and set our conventions within that.

The following is an example of setting all string properties to have a maximum length of 300 characters.

using RoundTheCode.EfCore.Entities;
 
namespace RoundTheCode.EfCore
{
	public class EfCoreDbContext : DbContext
	{
		...

		protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
		{
			// Pre-convention model configuration goes here
			configurationBuilder.Properties<string>().AreUnicode(true).HaveMaxLength(300);
		}

		...
	}
}

Once we've set this up, we then need to add a migration for it. The migration will update all our string property to ensure that those columns have a maximum length of 300 characters.

Add-Migration Conventions

Afterwards, it's a case of updating the database, either through migration bundles or by running the Update-Database command in Package Manager Console.

Of course, if we have explicitly defined a convention for a particular property in an entity, this will take precedence over anything in the ConfigureConventions method.

See EF Core updates in action

Check out our video where we showcase these EF Core 6 updates in action:

In addition, if you can download the code example to try out these EF Core 6 updates for yourself.