Training
Module
Use a database with minimal API, Entity Framework Core, and ASP.NET Core - Training
Learn how to add a database to a minimal API application.
This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
The following list includes the major new features in EF Core 5.0. For the full list of issues in the release, see our issue tracker.
As a major release, EF Core 5.0 also contains several breaking changes, which are API improvements or behavioral changes that may have negative impact on existing applications.
EF Core 5.0 supports many-to-many relationships without explicitly mapping the join table.
For example, consider these entity types:
public class Post
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Tag> Tags { get; set; }
}
public class Tag
{
public int Id { get; set; }
public string Text { get; set; }
public ICollection<Post> Posts { get; set; }
}
EF Core 5.0 recognizes this as a many-to-many relationship by convention, and automatically creates a PostTag
join table in the database. Data can be queried and updated without explicitly referencing the join table, considerably simplifying code. The join table can still be customized and queried explicitly if needed.
For further information, see the full documentation on many-to-many.
Starting with EF Core 3.0, EF Core always generates a single SQL query for each LINQ query. This ensures consistency of the data returned within the constraints of the transaction mode in use. However, this can become very slow when the query uses Include
or a projection to bring back multiple related collections.
EF Core 5.0 now allows a single LINQ query including related collections to be split into multiple SQL queries. This can significantly improve performance, but can result in inconsistency in the results returned if the data changes between the two queries. Serializable or snapshot transactions can be used to mitigate this and achieve consistency with split queries, but that may bring other performance costs and behavioral difference.
For example, consider a query that pulls in two levels of related collections using Include
:
var artists = await context.Artists
.Include(e => e.Albums)
.ToListAsync();
By default, EF Core will generate the following SQL when using the SQLite provider:
SELECT a."Id", a."Name", a0."Id", a0."ArtistId", a0."Title"
FROM "Artists" AS a
LEFT JOIN "Album" AS a0 ON a."Id" = a0."ArtistId"
ORDER BY a."Id", a0."Id"
With split queries, the following SQL is generated instead:
SELECT a."Id", a."Name"
FROM "Artists" AS a
ORDER BY a."Id"
SELECT a0."Id", a0."ArtistId", a0."Title", a."Id"
FROM "Artists" AS a
INNER JOIN "Album" AS a0 ON a."Id" = a0."ArtistId"
ORDER BY a."Id"
Split queries can be enabled by placing the new AsSplitQuery
operator anywhere in your LINQ query, or globally in your model's OnConfiguring
. For further information, see the full documentation on split queries.
EF Core 5.0 introduces a simple way to set up logging via the new LogTo
method. The following will cause logging messages to be written to the console, including all SQL generated by EF Core:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.LogTo(Console.WriteLine);
In addition, it is now possible to call ToQueryString
on any LINQ query, retrieving the SQL that the query would execute:
Console.WriteLine(
ctx.Artists
.Where(a => a.Name == "Pink Floyd")
.ToQueryString());
Finally, various EF Core types have been fitted with an enhanced DebugView
property which provides a detailed view into the internals. For example, ChangeTracker.DebugView can be consulted to see exactly which entities are being tracked in a given moment.
For further information, see the documentation on logging and interception.
The Include
method now supports filtering of the entities included:
var blogs = await context.Blogs
.Include(e => e.Posts.Where(p => p.Title.Contains("Cheese")))
.ToListAsync();
This query will return blogs together with each associated post, but only when the post title contains "Cheese".
For further information, see the full documentation on filtered include.
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 Name { get; set; }
}
public class Cat : Animal
{
public string EducationLevel { get; set; }
}
public class Dog : Animal
{
public string FavoriteToy { get; set; }
}
With TPT, a database table is created for each type in the hierarchy:
CREATE TABLE [Animals] (
[Id] int NOT NULL IDENTITY,
[Name] nvarchar(max) NULL,
CONSTRAINT [PK_Animals] PRIMARY KEY ([Id])
);
CREATE TABLE [Cats] (
[Id] int NOT NULL,
[EducationLevel] nvarchar(max) NULL,
CONSTRAINT [PK_Cats] PRIMARY KEY ([Id]),
CONSTRAINT [FK_Cats_Animals_Id] FOREIGN KEY ([Id]) REFERENCES [Animals] ([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,
);
For further information, see the full documentation on TPT.
Entity types are commonly mapped to tables or views such that EF Core will pull back the contents of the table or view when querying for that type. EF Core 5.0 adds additional mapping options, where an entity can be mapped to a SQL query (called a "defining query"), or to a table-valued function (TVF):
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>().ToSqlQuery(
@"SELECT Id, Name, Category, BlogId FROM posts
UNION ALL
SELECT Id, Name, ""Legacy"", BlogId from legacy_posts");
modelBuilder.Entity<Blog>().ToFunction("BlogsReturningFunction");
}
Table-valued functions can also be mapped to a .NET method rather than to a DbSet, allowing parameters to be passed; the mapping can be set up with HasDbFunction.
Finally, it is now possible to map an entity to a view when querying (or to a function or defining query), but to a table when updating:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<Blog>()
.ToTable("Blogs")
.ToView("BlogsView");
}
EF Core 5.0 allows the same CLR type to be mapped to multiple different entity types; such types are known as shared-type entity types. While any CLR type can be used with this feature, .NET Dictionary
offers a particularly compelling use-case which we call "property bags":
public class ProductsContext : DbContext
{
public DbSet<Dictionary<string, object>> Products => Set<Dictionary<string, object>>("Product");
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.SharedTypeEntity<Dictionary<string, object>>("Product", b =>
{
b.IndexerProperty<int>("Id");
b.IndexerProperty<string>("Name").IsRequired();
b.IndexerProperty<decimal>("Price");
});
}
}
These entities can then be queried and updated just like normal entity types with their own, dedicated CLR type. More information can be found in the documentation on property bags.
In EF Core 3.1, the dependent end of a one-to-one relationship was always considered optional. This was most apparent when using owned entities, as all the owned entity's column were created as nullable in the database, even if they were configured as required in the model.
In EF Core 5.0, a navigation to an owned entity can be configured as a required dependent. For example:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>(b =>
{
b.OwnsOne(e => e.HomeAddress,
b =>
{
b.Property(e => e.City).IsRequired();
b.Property(e => e.Postcode).IsRequired();
});
b.Navigation(e => e.HomeAddress).IsRequired();
});
}
EF Core 5.0 introduces AddDbContextFactory
and AddPooledDbContextFactory
to register a factory for creating DbContext instances in the application's dependency injection (D.I.) container; this can be useful when application code needs to create and dispose context instances manually.
services.AddDbContextFactory<SomeDbContext>(b =>
b.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test"));
At this point, application services such as ASP.NET Core controllers can then be injected with IDbContextFactory<TContext>
, and use it to instantiate context instances:
public class MyController : Controller
{
private readonly IDbContextFactory<SomeDbContext> _contextFactory;
public MyController(IDbContextFactory<SomeDbContext> contextFactory)
=> _contextFactory = contextFactory;
public void DoSomeThing()
{
using (var context = _contextFactory.CreateDbContext())
{
// ...
}
}
}
For further information, see the full documentation on DbContextFactory.
Compared to other databases, SQLite is relatively limited in its schema manipulation capabilities; for example, dropping a column from an existing table is not supported. EF Core 5.0 works around these limitations by automatically creating a new table, copying the data from the old table, dropping the old table and renaming the new one. This "rebuilds" the table, and allows previously unsupported migration operations to be safely applied.
For details on which migration operations are now supported via table rebuilds, see this documentation page.
EF Core 5.0 introduces support for specifying text collations at the database, column or query level. This allows case sensitivity and other textual aspects to be configured in a way that is both flexible and does not compromise query performance.
For example, the following will configure the Name
column to be case-sensitive on SQL Server, and any indexes created on the column will function accordingly:
modelBuilder
.Entity<User>()
.Property(e => e.Name)
.UseCollation("SQL_Latin1_General_CP1_CS_AS");
For further information, see the full documentation on collations and case sensitivity.
EF Core 5.0 exposes event counters which can be used to track your application's performance and spot various anomalies. Simply attach to a process running EF with the dotnet-counters tool:
> dotnet counters monitor Microsoft.EntityFrameworkCore -p 49496
[Microsoft.EntityFrameworkCore]
Active DbContexts 1
Execution Strategy Operation Failures (Count / 1 sec) 0
Execution Strategy Operation Failures (Total) 0
Optimistic Concurrency Failures (Count / 1 sec) 0
Optimistic Concurrency Failures (Total) 0
Queries (Count / 1 sec) 1,755
Queries (Total) 98,402
Query Cache Hit Rate (%) 100
SaveChanges (Count / 1 sec) 0
SaveChanges (Total) 1
For further information, see the full documentation on event counters.
[BackingField]
attribute.bool
).[Index]
attribute can be used on an entity type to specify an index, instead of using the Fluent API.[Keyless]
attribute can be used to configure an entity type as having no key.GroupBy(o => o.OrderDate).Select(g => g.Count(i => i.OrderDate != null))
).Reverse
.DateTime
for SQL Server (e.g. DateDiffWeek
, DateFromParts
).Contains
, Length
, SequenceEqual
).FirstOrDefault
over strings.DATALENGTH
function can now be called in queries via the new EF.Functions.DataLength
method.EnableDetailedErrors
adds additional details to exceptions.SaveChanges
is called and a transaction is already in progress, and roll back to it in case of failure.dotnet ef migrations list
command now shows which migrations have not yet been applied to the database (Get-Migration
does the same in the Package Management Console).--connection
parameter for specifying the connection string.People
and Addresses
will be scaffolded to entity types called Person
and Address
. Original database names can still be preserved.--no-onconfiguring
option can instruct EF Core to exclude OnConfiguring
when scaffolding a model.WithPartitionKey
method allows the Azure Cosmos DB partition key to be included both in the model and in queries.Contains
, StartsWith
and EndsWith
are now translated for Azure Cosmos DB.is
operator is now translated on Azure Cosmos DB.SaveChanges
is called.Clear()
method is more efficient and robust than mass-detaching all entities.ASPNETCORE_ENVIRONMENT
and DOTNET_ENVIRONMENT
environment variables to "Development". This brings the experience when using the generic host in line with the experience for ASP.NET Core during development..NET feedback
.NET is an open source project. Select a link to provide feedback:
Training
Module
Use a database with minimal API, Entity Framework Core, and ASP.NET Core - Training
Learn how to add a database to a minimal API application.