Announcing Entity Framework Core (EF Core) 5.0 Preview 8

Jeremy Likness

Today, the Entity Framework Core team announces the eighth and final preview release of EF Core 5.0. The next release will be a release candidate (RC). This release includes table-per-type (TPT) mapping, table-valued functions, SQLite table rebuilds for migrations and much more.

                     _/\__
               ---==/    \\
         ___  ___   |.    \|\
        | __|| __|  |  )   \\\
        | _| | _|   \_/ |  //|\\
        |___||_|       /   \\\/\\

  _______ _                 _                                    
 |__   __| |               | |                                   
    | |  | |__   __ _ _ __ | | __  _   _  ___  _   _             
    | |  | '_ \ / _` | '_ \| |/ / | | | |/ _ \| | | |            
    | |  | | | | (_| | | | |   <  | |_| | (_) | |_| |  _   _   _ 
    |_|  |_| |_|\__,_|_| |_|_|\_\  \__, |\___/ \__,_| (_) (_) (_)
                                    __/ |                        
                                   |___/

The EF Core team gives a warm thanks to the nearly 100 community contributors to EF Core 5.0.

Prerequisites

EF Core 5.0 will not run on .NET Standard 2.0 platforms, including .NET Framework.

  • The previews of EF Core 5.0 require .NET Standard 2.1.
  • This means that EF Core 5.0 will run on .NET Core 3.1 and does not require .NET 5.

To summarize: EF Core 5.0 runs on platforms that support .NET Standard 2.1.

The product will maintain .NET Standard 2.1 compatibility through the final release.


How to get EF Core 5.0 previews

EF Core is distributed exclusively as a set of NuGet packages. For example, to add the SQL Server provider to your project, you can use the following command using the dotnet tool:

dotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 5.0.0-preview.8.20407.4

This following table links to the preview 8 versions of the EF Core packages and describes what they are used for.

Package Purpose
Microsoft.EntityFrameworkCore The main EF Core package that is independent of specific database providers
Microsoft.EntityFrameworkCore.SqlServer Database provider for Microsoft SQL Server and SQL Azure
Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite SQL Server support for spatial types
Microsoft.EntityFrameworkCore.Sqlite Database provider for SQLite that includes the native binary for the database engine
Microsoft.EntityFrameworkCore.Sqlite.Core Database provider for SQLite without a packaged native binary
Microsoft.EntityFrameworkCore.Sqlite.NetTopologySuite SQLite support for spatial types
Microsoft.EntityFrameworkCore.Cosmos Database provider for Azure Cosmos DB
Microsoft.EntityFrameworkCore.InMemory The in-memory database provider
Microsoft.EntityFrameworkCore.Tools EF Core PowerShell commands for the Visual Studio Package Manager Console; use this to integrate tools like scaffolding and migrations with Visual Studio
Microsoft.EntityFrameworkCore.Design Shared design-time components for EF Core tools
Microsoft.EntityFrameworkCore.Proxies Lazy-loading and change-tracking proxies
Microsoft.EntityFrameworkCore.Abstractions Decoupled EF Core abstractions; use this for features like extended data annotations defined by EF Core
Microsoft.EntityFrameworkCore.Relational Shared EF Core components for relational database providers
Microsoft.EntityFrameworkCore.Analyzers C# analyzers for EF Core

Installing the EF Core Command Line Interface (CLI)

As with EF Core 3.0 and 3.1, the EF Core CLI is no longer included in the .NET Core SDK. Before you can execute EF Core migration or scaffolding commands, you’ll have to install this package as either a global or local tool.

dotnet-ef

To install the preview tool globally the first time, use:

dotnet tool install --global dotnet-ef --version 5.0.0-preview.8.20407.4

If you already have the tool installed, update it with:

dotnet tool update --global dotnet-ef --version 5.0.0-preview.8.20407.4

It’s possible to use this new version of the EF Core CLI with projects that use older versions of the EF Core runtime.


What’s New in EF Core 5 Preview 8

We maintain documentation covering new features introduced into each preview.

Some of the highlights from preview 8 are called out below. This preview also includes several bug fixes.

Table-per-type (TPT) mapping

By default, EF Core maps an inheritance hierarchy of .NET types to a single database table. This is known as table-per-hierarchy (TPH) mapping. EF Core 5.0 also allows mapping each .NET type in an inheritance hierarchy to a different database table; known as table-per-type (TPT) mapping.

For example, consider this model with a mapped hierarchy:

public class Animal
{
    public int Id { get; set; }
    public string Species { get; set; }
}

public class Pet : Animal
{
    public string Name { get; set; }
}

public class Cat : Pet
{
    public string EdcuationLevel { get; set; }
}

public class Dog : Pet
{
    public string FavoriteToy { get; set; }
}

By default, EF Core will map this to a single table:

CREATE TABLE [Animals] (
    [Id] int NOT NULL IDENTITY,
    [Species] nvarchar(max) NULL,
    [Discriminator] nvarchar(max) NOT NULL,
    [Name] nvarchar(max) NULL,
    [EdcuationLevel] nvarchar(max) NULL,
    [FavoriteToy] nvarchar(max) NULL,
    CONSTRAINT [PK_Animals] PRIMARY KEY ([Id])
);

However, mapping each entity type to a different table will instead result in one table per type:

CREATE TABLE [Animals] (
    [Id] int NOT NULL IDENTITY,
    [Species] nvarchar(max) NULL,
    CONSTRAINT [PK_Animals] PRIMARY KEY ([Id])
);

CREATE TABLE [Pets] (
    [Id] int NOT NULL,
    [Name] nvarchar(max) NULL,
    CONSTRAINT [PK_Pets] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_Pets_Animals_Id] FOREIGN KEY ([Id]) REFERENCES [Animals] ([Id]) ON DELETE NO ACTION
);

CREATE TABLE [Cats] (
    [Id] int NOT NULL,
    [EdcuationLevel] nvarchar(max) NULL,
    CONSTRAINT [PK_Cats] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_Cats_Animals_Id] FOREIGN KEY ([Id]) REFERENCES [Animals] ([Id]) ON DELETE NO ACTION,
    CONSTRAINT [FK_Cats_Pets_Id] FOREIGN KEY ([Id]) REFERENCES [Pets] ([Id]) ON DELETE NO ACTION
);

CREATE TABLE [Dogs] (
    [Id] int NOT NULL,
    [FavoriteToy] nvarchar(max) NULL,
    CONSTRAINT [PK_Dogs] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_Dogs_Animals_Id] FOREIGN KEY ([Id]) REFERENCES [Animals] ([Id]) ON DELETE NO ACTION,
    CONSTRAINT [FK_Dogs_Pets_Id] FOREIGN KEY ([Id]) REFERENCES [Pets] ([Id]) ON DELETE NO ACTION
);

Note that creation of the foreign key constraints shown above were added after branching the code for preview 8.

Entity types can be mapped to different tables using mapping attributes:

[Table("Animals")]
public class Animal
{
    public int Id { get; set; }
    public string Species { get; set; }
}

[Table("Pets")]
public class Pet : Animal
{
    public string Name { get; set; }
}

[Table("Cats")]
public class Cat : Pet
{
    public string EdcuationLevel { get; set; }
}

[Table("Dogs")]
public class Dog : Pet
{
    public string FavoriteToy { get; set; }
}

Or using ModelBuilder configuration:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Animal>().ToTable("Animals");
    modelBuilder.Entity<Pet>().ToTable("Pets");
    modelBuilder.Entity<Cat>().ToTable("Cats");
    modelBuilder.Entity<Dog>().ToTable("Dogs");
}

Documentation is tracked by issue #1979.

Migrations: Rebuild SQLite tables

Compared to other database, SQLite is relatively limited in its schema manipulation capabilities. For example, dropping a column from an existing table requires that the entire table be dropped and re-created. EF Core 5.0 Migrations now supports automatic rebuilding the table for schema changes that require it.

For example, imagine we have a Unicorns table created for a Unicorn entity type:

public class Unicorn
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}
CREATE TABLE "Unicorns" (
    "Id" INTEGER NOT NULL CONSTRAINT "PK_Unicorns" PRIMARY KEY AUTOINCREMENT,
    "Name" TEXT NULL,
    "Age" INTEGER NOT NULL
);

We then learn that storing the age of a unicorn is considered very rude, so let’s remove that property, add a new migration, and update the database. This update will fail when using EF Core 3.1 because the column cannot be dropped. In EF Core 5.0, Migrations will instead rebuild the table:

CREATE TABLE "ef_temp_Unicorns" (
    "Id" INTEGER NOT NULL CONSTRAINT "PK_Unicorns" PRIMARY KEY AUTOINCREMENT,
    "Name" TEXT NULL
);

INSERT INTO "ef_temp_Unicorns" ("Id", "Name")
SELECT "Id", "Name"
FROM Unicorns;

PRAGMA foreign_keys = 0;

DROP TABLE "Unicorns";

ALTER TABLE "ef_temp_Unicorns" RENAME TO "Unicorns";

PRAGMA foreign_keys = 1;

Notice that:

  • A temporary table is created with the desired schema for the new table
  • Data is copied from the current table into the temporary table
  • Foreign key enforcement is switched off
  • The current table is dropped
  • The temporary table is renamed to be the new table

Documentation is tracked by issue #2583.

Table-valued functions

This feature was contributed from the community by @pmiddleton. Many thanks for the contribution!

EF Core 5.0 includes first-class support for mapping .NET methods to table-valued functions (TVFs). These functions can then be used in LINQ queries where additional composition on the results of the function will also be translated to SQL.

For example, consider this TVF defined in a SQL Server database:

create FUNCTION GetReports(@employeeId int)
RETURNS @reports TABLE
(
    Name nvarchar(50) not null,
    IsDeveloper bit not null
)
AS
begin
    WITH cteEmployees AS
    (
        SELECT id, name, managerId, isDeveloper
        FROM employees
        WHERE id = @employeeId
        UNION ALL
        SELECT e.id, e.name, e.managerId, e.isDeveloper
        FROM employees e
        INNER JOIN cteEmployees cteEmp ON cteEmp.id = e.ManagerId
    )

    insert into @reports
    select name, isDeveloper
    FROM cteEmployees
    where id != @employeeId

    return
end

The EF Core model requires two entity types to use this TVF:

  • An Employee type that maps to the Employees table in the normal way
  • A Report type that matches the shape returned by the TVF
public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }
    public bool IsDeveloper { get; set; }

    public int? ManagerId { get; set; }
    public virtual Employee Manager { get; set; }
}
public class Report
{
    public string Name { get; set; }
    public bool IsDeveloper { get; set; }
}

These types must be included in the EF Core model:

modelBuilder.Entity<Employee>();
modelBuilder.Entity(typeof(Report)).HasNoKey();

Notice that Report has no primary key and so must be configured as such.

Finally, a .NET method must be mapped to the TVF in the database. This method can be defined on the DbContext using the new FromExpression method:

public IQueryable<Report> GetReports(int managerId)
    => FromExpression(() => GetReports(managerId));

This method uses a parameter and return type that match the TVF defined above. The method is then added to the EF Core model in OnModelCreating:

modelBuilder.HasDbFunction(() => GetReports(0));

(Using a lambda here is an easy way to pass the MethodInfo to EF Core. The arguments passed to the method are ignored.)

We can now write queries that call GetReports and compose over the results. For example:

from e in context.Employees
from rc in context.GetReports(e.Id)
where rc.IsDeveloper == true
select new
{
  ManagerName = e.Name,
  EmployeeName = rc.Name,
})

On SQL Server, this translates to:

SELECT [e].[Name] AS [ManagerName], [g].[Name] AS [EmployeeName]
FROM [Employees] AS [e]
CROSS APPLY [dbo].[GetReports]([e].[Id]) AS [g]
WHERE [g].[IsDeveloper] = CAST(1 AS bit)

Notice that the SQL is rooted in the Employees table, calls GetReports, and then adds an additional WHERE clause on the results of the function.

Flexible query/update mapping

EF Core 5.0 allows mapping the same entity type to different database objects. These objects may be tables, views, or functions.

For example, an entity type can be mapped to both a database view and a database table:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<Blog>()
        .ToTable("Blogs")
        .ToView("BlogsView");
}

By default, EF Core will then query from the view and send updates to the table. For example, executing the following code:

var blog = context.Set<Blog>().Single(e => e.Name == "One Unicorn");

blog.Name = "1unicorn2";

context.SaveChanges();

Results in a query against the view, and then an update to the table:

SELECT TOP(2) [b].[Id], [b].[Name], [b].[Url]
FROM [BlogsView] AS [b]
WHERE [b].[Name] = N'One Unicorn'

SET NOCOUNT ON;
UPDATE [Blogs] SET [Name] = @p0
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

Context-wide split-query configuration

Split queries (see below) can now be configured as the default for any query executed by the DbContext. This configuration is only available for relational providers, and so must be specified as part of the UseProvider configuration. For example:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .UseSqlServer(
            Your.SqlServerConnectionString,
            b => b.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery));

Documentation is tracked by issue #2479.

PhysicalAddress mapping

This feature was contributed from the community by @ralmsdeveloper. Many thanks for the contribution!

The standard .NET PhysicalAddress class is now automatically mapped to a string column for databases that do not already have native support. For more information, see the examples for IPAddress below.


Daily builds

EF Core previews are aligned with .NET 5 previews. These previews tend to lag behind the latest work on EF Core. Consider using the daily builds instead to get the most up-to-date EF Core features and bug fixes.

As with the previews, the daily builds do not require .NET 5; they can be used with GA/RTM release of .NET Core 3.1.


Contribute to .NET 5

The .NET documentation team is reorganizing .NET content to better match the workloads you build with .NET. This includes a new .NET Data landing page that will link out to data-related topics ranging from EF Core to APIs, Big Data, and Machine learning. The planning and execution will be done completely in the open on GitHub. This is your opportunity to help shape the hierarchy and content to best fit your needs as a .NET developer. We look forward to your contributions!

The EF Core Community Standup

The EF Core team is now live streaming every other Wednesday at 10am Pacific Time, 1pm Eastern Time, or 17:00 UTC. Join the stream to ask questions about the EF Core topic of your choice, including the latest preview release.

Documentation and Feedback

The starting point for all EF Core documentation is docs.microsoft.com/ef/.

Please file issues found and any other feedback on the dotnet/efcore GitHub repo.

The following short links are provided for easy reference and access.

Main documentation: https://aka.ms/efdocs

Issues and feature requests for EF Core: https://aka.ms/efcorefeedback

Entity Framework Roadmap: https://aka.ms/efroadmap

What’s new in EF Core 5.x? https://aka.ms/efcore5


Thank you from the team

A big thank you from the EF team to everyone who has used EF over the years!

ajcvickers Arthur Vickers AndriySvyryd Andriy Svyryd Brice Lambson JeremyLikness Jeremy Likness
maumar Maurycy Markowski roji Shay Rojansky smitpatel Smit Patel  

Thank you to our contributors!

A big thank you to the following community members who have already contributed code or documentation to the EF Core 5 release! (List is in chronological order of first contribution to EF Core 5).

divega
Diego Vega
lajones
lajones
aevitas aevitas alaatm Alaa Masoud aleksandar-manukov Aleksandar Manukov amrbadawy Amr Badawy
AnthonyMonterrosa Anthony Monterrosa bbrandt Ben Brandt benmccallum Ben McCallum ccjx Clarence Cai
CGijbels Christophe Gijbels cincuranet Jiri Cincura Costo Vincent Costel dshuvaev Dmitry Shuvaev
EricStG Eric St-Georges ErikEJ Erik Ejlskov Jensen gravbox Christopher Davis ivaylokenov Ivaylo Kenov
jfoshee Jacob Foshee jmzagorski Jeremy Zagorski jviau Jacob Viau knom Max K.
lohoris-crane lohoris-crane loic-sharma Loïc Sharma lokalmatador lokalmatador mariusGundersen Marius Gundersen
Marusyk Roman Marusyk matthiaslischka Matthias Lischka MaxG117 MaxG117 MHDuke MHDuke
mikes-gh Mike Surcouf Muppets Neil Bostrom nmichels Nícolas Michels OOberoi Obi Oberoi
orionstudt Josh Studt ozantopal Ozan Topal pmiddleton Paul Middleton prog-rajkamal Raj
ptjhuang Peter Huang ralmsdeveloper Rafael Almeida Santos redoz Patrik Husfloen rmarskell Richard Marskell
sguitardude sguitardude SimpleSamples Sam Hobbs svengeance Sven VladDragnea Vlad
vslee vslee WeihanLi liweihan Youssef1313 Youssef Victor 1iveowl 1iveowl
thomaslevesque Thomas Levesque akovac35 Aleksander Kovač leotsarev Leonid Tsarev kostat Konstantin Triger
sungam3r Ivan Maximov dzmitry-lahoda Dzmitry Lahoda Logerfo Bruno Logerfo witheej Josh Withee
FransBouma Frans Bouma IGx89 Matthew Lieder paulomorgado Paulo Morgado mderriey Mickaël Derriey
LaurenceJKing Laurence King oskarj Oskar Josefsson bdebaere bdebaere BhargaviAnnadevara-MSFT Bhargavi Annadevara
AlexanderTaeschner Alexander Täschner Jesse-Hufstetler Jesse Hufstetler ivarlovlie Ivar Løvlie cucoreanu cucoreanu
serpent5 Kirk Larkin sdanyliv Svyatoslav Danyliv twenzel Toni Wenzel manvydasu manvydasu
brandongregoryscott Brandon Scott uncheckederror Thomas Ryan rocke97 Aaron Gunther jonlouie Jon Louie
mohsinnasir Mohsin Nasir seekingtheoptimal Bálint Szabó MartinBP Martin Boye Petersen Ropouser Duje Đaković< /td>
codemillmatt Matt Soucoup shahabganji Saeed Ganji AshkanAbd Ashkan Abd ChristopherHaws Christopher Haws
SergerGood Sergei Khlebnikov KaloyanIT Kaloyan Kostov bide45 bide45 jsportaro Joseph Portaro
mikewodarczyk Mike Wodarczyk jeffsvajlenko Jeffrey Svajlenko vanillajonathan Jonathan m4ss1m0g Massimo Giambona
Psypher9 Turner Bass

3 comments

Discussion is closed. Login to edit/delete existing comments.

  • Michael Taylor 0

    Please fix broken links in article. The section on query splitting doesn’t talk at all about what this means. The only link it has says it is for the documentation but if you click on it then you go to an (https://github.com/dotnet/EntityFramework.Docs/issues/2407)[issue] that says Document IndexAttribute which doesn’t seem at all related.

    The next section mentions the PhysicalAddress class and provides a link but that link generates a 404. It could just be the main blog site hasn’t updated yet in my area.

    • Jeremy LiknessMicrosoft employee 0

      Thanks for bring this to our attention Michael. The links are updated.

Feedback usabilla icon