What's New in EF Core 5
Last updated
Last updated
Improve EF Core performance with EF Extensions
EF Core 5.0 is currently in development, and here is the list of all the interesting changes introduced so far in each preview.
The simple logging feature adds functionality similar to Database.Log
in EF6 by providing a simple way to get logs from EF Core without the need to configure any kind of external logging framework.
EF Core 5.0 introduces the ToQueryString
extension method, which will return the SQL that EF Core will generate when executing a LINQ query.
An entity type can now be configured as having no key using the new KeylessAttribute
.
It is now easier to create a DbContext
instance without any connection or connection string.
The connection or connection string can now be mutated on the context instance.
This feature allows the same context instance to dynamically connect to different databases.
EF Core can now generate runtime proxies that automatically implement INotifyPropertyChanging
and INotifyPropertyChanged
.
These then report value changes on entity properties directly to EF Core, avoiding the need to scan for changes.
However, proxies come with their own set of limitations, so they are not for everyone.
Debug views are an easy way to look at the internals of EF Core when debugging issues. A debug view for the Model was implemented some time ago. For EF Core 5.0, we have made the model view easier to read and added a new debug view for tracked entities in the state manager.
Relational databases typically treat NULL as an unknown value and therefore not equal to any other NULL.
While C# treats null as a defined value, which compares equal to any other null.
EF Core by default translates queries so that they use C# null semantics. EF Core 5.0 greatly improves the efficiency of these translations.
EF Core 5.0 supports the mapping of C# indexer properties. These properties allow entities to act as property bags where columns are mapped to named properties in the bag.
EF Core 5.0 migrations can now generate CHECK
constraints for enum
property mappings.
A new IsRelational
method has been added in addition to the existing IsSqlServer
, IsSqlite
and IsInMemory
. This method can be used to test if the DbContext is using any relational database provider.
The Azure Cosmos DB database provider now supports optimistic concurrency using ETags
. Use the model builder in OnModelCreating
to configure an ETag
.
The SaveChanges
will then throw a DbUpdateConcurrencyException
on a concurrency conflict, which can be handled to implement retries, etc.
Queries containing new DateTime
construction is now translated.
Also, the following SQL Server functions are now mapped:
DateDiffWeek
DateFromParts
For example:
Queries using Contains
, Length
, SequenceEqual
, etc. on byte[]
properties are now translated to SQL.
Queries using Reverse
are now translated.
Queries using bit-wise operators are now translated in more cases.
Queries that use the string methods such as, Contains
, StartsWith
, and EndsWith
are now translated when using the Azure Cosmos DB provider.
A C# attribute can now be used to specify the backing field for a property. This attribute allows EF Core to still write to and read from the backing field as would normally happen, even when the backing field cannot be found automatically.
EF Core uses a discriminator column for TPH mapping of an inheritance hierarchy.
Some performance enhancements are possible as long as EF Core knows all possible values for the discriminator.
EF Core 5.0 now implements these enhancements.
For example, previous versions of EF Core would always generate this SQL for a query returning all types in a hierarchy.
EF Core 5.0 will now generate the following when a complete discriminator mapping is configured
It will be the default behavior starting with preview 3.
The following two performances improvements are made for SQLite:
Retrieving binary and string data with GetBytes
, GetChars
, and GetTextReader
is now more efficient by making use of SqliteBlob and streams.
The initialization of SqliteConnection
is now lazy.
These improvements are in the ADO.NET Microsoft.Data.Sqlite
provider and hence also improve performance outside of EF Core.
The Include
method now supports filtering of the entities included.
This query will return blogs together with each associated post, but only when the post title contains "Cheese".
Skip and Take can also be used to reduce the number of included entities.
This query will return blogs with at most five posts included on each blog.
Navigation properties are primarily configured when defining relationships. However, the new Navigation
method can be used in cases where navigation properties need an additional configuration. For example, to set a backing field for the navigation when the field would not be found by convention.
Note that the Navigation
API does not replace relationship configuration. Instead, it allows additional configuration of navigation properties in already discovered or defined relationships.
Migrations and scaffolding now allow namespaces to be specified on the command line. For example, to reverse engineer a database putting the context and model classes in different namespaces.
Also, a connection string can now be passed to the database-update
command.
Equivalent parameters have also been added to the PowerShell commands used in the VS Package Manager Console.
For performance reasons, EF doesn't do additional null-checks when reading values from the database. This can result in exceptions that are hard to root-cause when an unexpected null is encountered.
Using EnableDetailedErrors
will add extra null checking to queries such that, for a small performance overhead, these errors are easier to trace back to a root cause.
The partition key to use for a given query can now be specified in the query.
This can be accessed using the new EF.Functions.DataLength
method.
Precision and scale for a property can now be specified using the model builder.
Precision and scale can still be set via the full database type, such as "decimal(16,4)".
The fill factor can now be specified when creating an index on SQL Server. For example.
The default collation for a database can now be specified in the EF model. It will flow through to generated migrations to set the collation when the database is created.
When you create migrations then it generates the following to create the database on SQL Server.
You can also specify the collation to use for specific database columns.
For those not using migrations, collations are now reverse-engineered from the database when scaffolding a DbContext
.
Finally, the EF.Functions.Collate()
allows for ad-hoc queries using different collations.
This will generate the following query for SQL Server.
The ad-hoc collations should be used with care as they can negatively impact database performance.
Arguments now flow from the command line into the CreateDbContext
method of IDesignTimeDbContextFactory
. For example, to indicate this is a dev build, a custom argument (e.g. dev
) can be passed on the command line.
This argument will then flow into the factory, where it can be used to control how the context is created and initialized.
No-tracking queries can now be configured to perform identity resolution. For example, the following query will create a new Blog instance for each Post, even if each Blog has the same primary key.
However, at the expense of usually being slightly slower and always using more memory, this query can be changed to ensure only a single Blog instance is created.
It is only useful for no-tracking queries since all tracking queries already exhibit this behavior. Also, following the API review, the PerformIdentityResolution
the syntax will be changed.
Most databases allow computed column values to be stored after computation.
The computed column is calculated only once on the update, instead of each time its value is retrieved it takes up disk space.
This also allows the column to be indexed for some databases.
EF Core 5.0 allows computed columns to be configured as stored.
SQLite computed columns
EF Core now supports computed columns in SQLite databases.
Starting with EF Core 3.0, EF Core always generates a single SQL query for each LINQ query.
It ensures consistency of the data returned within the constraints of the transaction mode in use.
However, it 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.
It can significantly improve performance but can result in inconsistency in the results returned if the data changes between the two queries.
The 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 differences.
Split queries with Include
For example, consider a query that pulls in two levels of related collections using Include
method.
By default, EF Core will generate the following SQL when using the SQLite provider.
The new AsSplitQuery
API can be used to change this behavior.
The AsSplitQuery
is available for all relational database providers and can be used anywhere in the query, just like AsNoTracking
. EF Core will now generate the following three SQL queries.
All operations on the query root are supported including OrderBy
, Skip
, Take
, Join
, FirstOrDefault
and similar single result selecting operations.
The filtered Includes with OrderBy
, Skip
, Take
are not supported in preview 6, but are available in the daily builds and will be included in preview 7.
Split queries with collection projections
The AsSplitQuery
method can also be used when collections are loaded in projections.
The above LINQ query generates the following two SQL queries when using the SQLite provider
Only materialization of the collection is supported. Any composition after e.Albums
in the above case won't result in a split query.
The new IndexAttribute
can be placed on an entity type to specify an index for a single column.
For SQL Server, Migrations will then generate the following SQL.
IndexAttribute can also be used to specify an index spanning multiple columns.
For SQL Server, the result is as shown below.
We are continuing to improve the exception messages generated when query translation fails. For example, this query uses the IsSigned
unmapped property.
EF Core will throw the following exception indicating that translation failed because IsSigned
is not mapped.
Similarly, better exception messages are now generated when attempting to translate string comparisons with culture-dependent semantics. For example, the following query attempts to use StringComparison.CurrentCulture
.
EF Core will now throw the following exception.
EF Core exposes a transaction ID for the correlation of transactions across calls.
This ID is typically set by EF Core when a transaction is started.
If the application starts the transaction instead, then this feature allows the application to explicitly set the transaction ID so it is correlated correctly everywhere it is used.
The standard .NET IPAddress
class is now automatically mapped to a string column for databases that do not already have native support. For example, consider mapping this entity type.
On SQL Server, the migration will create the following table.
Entities can then be added in the normal way.
And the resulting SQL will insert the normalized IPv4 or IPv6 address.
When a DbContext
is scaffolded from an existing database, EF Core by default creates an OnConfiguring
overload with a connection string so that the context is immediately usable. However, this is not useful if you already have a partial class with OnConfiguring
, or if you are configuring the context some other way.
To address this, the scaffolding commands can now be instructed to omit the generation of OnConfiguring
.
Or in the Package Manager Console.
It is recommended to use a named connection string and secure storage like User Secrets.
The FirstOrDefault
and similar operators for characters in strings are now translated in a LINQ query.
It will be translated to the following SQL when using SQL Server.
EF Core now generates better queries with CASE blocks. Let's consider the following LINQ query.
Previously, the above LINQ would be translated to the following query on SQL Server.
But it is now translated to the following query.
EF Core 5.0 introduces AddDbContextFactory
and AddPooledDbContextFactory
to register a factory for creating DbContext
instances in the application's dependency injection container.
Application services such as ASP.NET Core controllers can then depend on IDbContextFactory<TContext>
in the service constructor.
DbContext
instances can then be created and used as needed.
The DbContext
instances created in this way are not managed by the application's service provider and therefore must be disposed of by the application.
This decoupling is very useful for Blazor applications, where using IDbContextFactory
is recommended, but may also be useful in other scenarios.
DbContext instances can be pooled by calling AddPooledDbContextFactory
.
This pooling works the same way as for AddDbContextPool
, and also has the same limitations.
EF Core 5.0 introduces ChangeTracker.Clear()
which clears the DbContext
of all tracked entities.
This should usually not be needed when using the best practice of creating a new, short-lived context instance for each unit-of-work.
However, if there is a need to reset the state of a DbContext
instance, then using the new Clear()
method is more performant and robust than mass-detaching all entities.
EF Core allows an explicit value to be set for a column that may also have default value constraints.
EF Core uses the CLR default of type property type as a sentinel for this; if the value is not the CLR default, then it is inserted, otherwise, the database default is used.
This creates problems for types where the CLR default is not a good sentinel--most notably, bool
properties.
EF Core 5.0 now allows the backing field to be nullable for cases like this.
The backing field is nullable, but the publicly exposed property is not.
It allows the sentinel value to be null
without impacting the public surface of the entity type.
In this case, if the IsValid
is never set, then the database default will be used since the backing field remains null.
If either true
or false
are set, then this value is saved explicitly to the database.
EF Core allows the Cosmos partition key is included in the EF model.
Starting with preview 7, the partition key is included in the entity type's PK and is used to improved performance in some queries.
EF Core 5.0 improves the configuration of Cosmos and Cosmos connections.
Previously, EF Core required the end-point and key to be specified explicitly when connecting to a Cosmos database.
EF Core 5.0 allows the use of a connection string instead.
In addition, EF Core 5.0 allows the WebProxy instance to be explicitly set.
Many other timeout values, limits, etc. can now also be configured.
Finally, the default connection mode is now ConnectionMode.Gateway
, which is generally more compatible.
Previously when scaffolding a DbContext from an existing database, EF Core will create entity type names that match the table names in the database. For example, tables People
and Addresses
resulted in entity types named People
and Addresses
.
In previous releases, this behavior was configurable through the registration of a pluralization service. Now in EF Core 5.0, the Humanizer package is used as a default pluralization service. This means tables People
and Addresses
will now be reverse engineered to entity types named Person
and Address
.
EF Core now supports savepoints for greater control over transactions that execute multiple operations.
Savepoints can be manually created, released, and rolled back.
In addition, EF Core will now roll back to the last savepoint when executing SaveChanges
fails. This allows SaveChanges to be re-tried without re-trying the entire transaction.
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.
By default, EF Core will map this to a single table.
However, mapping each entity type to a different table will instead result in one table per type.
The creation of the foreign key constraints shown above was added after branching the code for preview 8.
Entity types can be mapped to different tables using mapping attributes.
Or using ModelBuilder
configuration.
Compared to other databases 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 of the table for schema changes that require it.
For example, imagine we have a Unicorns
table created for a Unicorn
entity type.
It will translate to the following SQL.
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.
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
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.
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