Entity Framework Core has been improving yearly, bringing tools to simplify data access and management in .NET applications. Check out this post with 10 of the main new features in EF 9 for ASP.NET Core.
Entity Framework Core—EF Core—is the main object relational model (ORM) maintained by Microsoft. It has been continuously improved to meet the demands of developers and the needs of modern applications. Some of the main aspects include performance, developer productivity, scalability, mapping flexibility and others.
In this post, we will look at the 10 main new features of EF 9, which arrived with the release of .NET 9 in November 2024.
You can access the project with all the example code covered in the post in this GitHub repository: EF 9 news source code.
LINQ (Language Integrated Query) is a .NET feature that allows you to query different data sources directly in C# code, using a declarative syntax integrated into the language. In Version 9 of EF Core, we can highlight the following improvements, regarding LINQ query methods.
Until EF 8, the GroupBy
extension method was only available for properties. Now you can group complex types, such as classes and records.
Note the example below:
Here we have the Customer class that uses the Address record that is declared as a complex type, so in the ComplexTypesGroupByAsync()
method, the addresses are grouped by the Address
object (complex type). In this case, the key is all the properties of the object, and the quantity is the number of records present in that group.
The output in SQL in SQLServer is as follows:
Another method now available for complex types is ExecuteUpdate
. This method is used to perform bulk updates directly on the database, without loading the entities into memory, thus achieving better performance compared to the traditional approach of loading, modifying and saving entities.
Note the code below:
Here we are telling EF to update all customer addresses in the USA region with the values received by the new address. In this case, the SQL translation is as follows:
Previously, it was necessary to manually list the different properties of the complex type when calling the ExecuteUpdate
method, but now this is no longer necessary.
EF 9 has received an improvement to save database round trips.
Consider the following LINQ query:
In EF 8, two round-trip queries are executed, which are translated to SQL Server as follows:
In EF 9, the IQueryable
on usCustomers
is inlined, so a single database round trip is made:
This EF 9 approach saves resources because it only makes one round trip to the database, instead of two as in EF 8.
Support for array mapping and mutable lists of primitive types was introduced in EF 8, but this feature has been expanded in EF 9 to also include read-only collections/lists.
This feature helps deal with scenarios where collections of primitive types such as string, int or bool are prevented from being modified, something that previously required workarounds.
Note the example below:
The EventDays
property is of type ReadOnlyCollection<int>
in this case it cannot be modified, however, it can be used normally as shown in the code below:
Here we are using the values from the EventDays
list, but without modifying them. If we try to modify the list, we will receive the following error from the Visual Studio interpreter:
Note that collections of the ReadOnlyCollection
type do not have the Add()
extension method and others, precisely so that they are not modified.
Up until Version 8, EF Core sometimes produced SQL with elements that were not really needed. EF 9 removes most of these elements, producing more compact and, in some cases, more efficient SQL code.
Note the example below:
If we run the following query to get all Posts with at least one Author:
EF 8 would generate the following SQL:
Note that there is a LEFT JOIN
with TopAuthor
which for the query above becomes unnecessary.
EF 9 results in a polished SQL without the join:
Similar to the previous example, the query below is now optimized in EF 9.
In EF 8, the generated SQL is as follows:
Note that the [p].[Id]
column is used in the query, even though it is not necessary, since the upper select only counts records.
In EF 9 this has been improved, the projection is now empty. It may not seem like much, but depending on the scenario, it can simplify the SQL considerably.
In EF 8, the following LINQ query uses the SQL COUNT
function:
EF 9 generates a more efficient SQL translation using EXISTS
:
EF 9 has simplified the conversion of the Order
and OrderDescending
sort operations. They work similarly to the traditional OrderBy
/OrderByDescending
, but instead of using an argument passed as a parameter, they apply the default ordering. In the case of entities, the ordering is done based on primary key values. And for other types, the ordering is done based on their values.
Note the example below:
Here we are using the new Order
and OrderDescending
operators, which will produce the following SQL:
The same ordering could be done as follows using OrderBy
and OrderByDescending
:
An important point is that the Order
and OrderDescending
methods are only compatible with collections of entities, complex types or scalars. For more complex projections such as collections of anonymous types containing several properties, for example, these methods will not work.
Queries that use the logical negation operator !
have a more efficient translation in EF 9. Note the example below:
The result in EF 8 is translated into nested CASE blocks:
In EF 9, nesting has been removed:
SQL Server’s HierarchyId
was added in EF 8 and has been improved in EF 9. A new method has been added to make it easier to create new child nodes in a tree structure.
Note the example below.
Here we are filtering on a person whose name is John. We can use this HierarchyId
property to create child nodes simply, without any explicit character manipulation:
In this case, John has a HierarchyId of /4/1/3/1/
, child1 will get the HierarchyId /4/1/3/1/1/1/1/
and child2 will get the HierarchyId /4/1/3/1/2/
.
Another possibility is to create a node between these two children, in which case an additional sublevel can be used:
This will create a node with a HierarchyId of /4/1/3/1/1.5/
, with 1.5
, which puts it between child1 and child2.
EF 9 has added new methods to populate the database with initial data, namely UseSeeding
and UseAsyncSeeding
.
These methods aim to unify all the data propagation code in one place. In addition, both methods are protected by the migration lock mechanism to avoid concurrency issues.
Below is an example of how to use the new methods. Note that checking for the existence of records is done manually through a simple query:
When a document is stored in an Azure Cosmos DB database, it is assigned a unique ID. Another feature available in Azure Cosmos DB is that each document can contain a “partition key” that determines the logical partitioning of the data, providing efficient scaling.
In EF 9, the provider received improvements to identify partition key comparisons in LINQ queries and extract them to ensure that queries are sent only to the relevant partition, greatly improving performance. This is because the query does not use all the available data, but only that which is part of the partition related to the query.
Note the query below:
In this query, the Cosmos DB provider automatically recognizes the comparison on PartitionKey
. In the logs, we can see the following:
In previous versions, the comparison was left in the WHERE clause using the PartitionKey
, this caused the query to ignore the relevant partition and execute the query on all partitions resulting in increased costs and reduced performance.
Now in EF 9, this comparison has been deprecated and is used to execute the query only on the relevant partition.
Another important point is that if the query has an ID for the document and does not include any other query operations, the provider may apply an additional optimization. Note the query below, passing the book ID:
If we look at the logs, we see the following:
Notice that no SQL query was sent. Instead, the provider performs a point read (ReadItem API), which directly fetches the document using the partition key and ID.
This is the most efficient and cost-effective type of read possible to perform on Azure Cosmos DB.
EF 9 brought many improvements to ASP.NET Core, many of which focus on improving the performance of database queries, while others bring more options for dealing with everyday tasks, such as database and table migrations, and record ordering, among others.
Whether you’re just starting out or already using EF Core, I recommend trying out these new features and evaluating how they can add value to your project.
In this post, we covered 10 of the main new features in EF 9. For a more comprehensive overview, check out the full list of changes and improvements in the official documentation: What’s New in EF 9. Or learn about vector search in our other post.