Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 32b32ea

Browse files
authored
What's New for preview 4 (#4718)
1 parent 6c7526f commit 32b32ea

File tree

152 files changed

+1329
-260
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

152 files changed

+1329
-260
lines changed

entity-framework/core/what-is-new/ef-core-9.0/whatsnew.md

Lines changed: 160 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: What's New in EF Core 9
33
description: Overview of new features in EF Core 9
44
author: ajcvickers
5-
ms.date: 03/07/2024
5+
ms.date: 05/02/2024
66
uid: core/what-is-new/ef-core-9.0/whatsnew
77
---
88

@@ -15,15 +15,113 @@ EF9 is available as [daily builds](https://github.com/dotnet/efcore/blob/main/do
1515
> [!TIP]
1616
> You can run and debug into the samples by [downloading the sample code from GitHub](https://github.com/dotnet/EntityFramework.Docs). Each section below links to the source code specific to that section.
1717
18-
EF9 targets .NET 8, and can therefore be used with either [.NET 8 (LTS)](https://dotnet.microsoft.com/download/dotnet/8.0) or a .NET 9 preview.
18+
EF9 targets .NET 8, and can therefore be used with either [.NET 8 (LTS)](https://dotnet.microsoft.com/download/dotnet/8.0) or a [.NET 9 preview](https://dotnet.microsoft.com/download/dotnet/9.0).
1919

2020
> [!TIP]
2121
> The _What's New_ docs are updated for each preview. All the samples are set up to use the [EF9 daily builds](https://github.com/dotnet/efcore/blob/main/docs/DailyBuilds.md), which usually have several additional weeks of completed work compared to the latest preview. We strongly encourage use of the daily builds when testing new features so that you're not doing your testing against stale bits.
2222
23+
<a name="cosmos"></a>
24+
25+
## Azure Cosmos DB for NoSQL
26+
27+
We are working on significant updates in EF9 to the EF Core database provider for Azure Cosmos DB for NoSQL.
28+
29+
### Role-based access
30+
31+
Azure Cosmos DB for NoSQL includes a [built-in role-based access control (RBAC) system](/azure/cosmos-db/role-based-access-control). This is now supported by EF9 for both management and use of containers. No changes are required to application code. See [Issue #32197](https://github.com/dotnet/efcore/issues/32197) for more information.
32+
33+
### Synchronous access blocked by default
34+
35+
> [!TIP]
36+
> The code shown here comes from [CosmosSyncApisSample.cs](https://github.com/dotnet/EntityFramework.Docs/tree/main/samples/core/Miscellaneous/NewInEFCore9.Cosmos/CosmosSyncApisSample.cs).
37+
38+
Azure Cosmos DB for NoSQL does not support synchronous (blocking) access from application code. Previously, EF masked this by default by blocking for you on async calls. However, this both encourages sync use, which is bad practice, and [may cause deadlocks](https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html). Therefore, starting with EF9, an exception is thrown when synchronous access is attempted. For example:
39+
40+
```output
41+
System.InvalidOperationException: An error was generated for warning 'Microsoft.EntityFrameworkCore.Database.SyncNotSupported':
42+
Azure Cosmos DB does not support synchronous I/O. Make sure to use and correctly await only async methods when using
43+
Entity Framework Core to access Azure Cosmos DB. See https://aka.ms/ef-cosmos-nosync for more information.
44+
This exception can be suppressed or logged by passing event ID 'CosmosEventId.SyncNotSupported' to the 'ConfigureWarnings'
45+
method in 'DbContext.OnConfiguring' or 'AddDbContext'.
46+
at Microsoft.EntityFrameworkCore.Diagnostics.EventDefinition.Log[TLoggerCategory](IDiagnosticsLogger`1 logger, Exception exception)
47+
at Microsoft.EntityFrameworkCore.Cosmos.Diagnostics.Internal.CosmosLoggerExtensions.SyncNotSupported(IDiagnosticsLogger`1 diagnostics)
48+
at Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal.CosmosClientWrapper.DeleteDatabase()
49+
at Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal.CosmosDatabaseCreator.EnsureDeleted()
50+
at Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade.EnsureDeleted()
51+
```
52+
53+
As the exception says, sync access can still be used for now by configuring the warning level appropriately. For example, in `OnConfiguring` on your `DbContext` type:
54+
55+
```csharp
56+
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
57+
=> optionsBuilder.ConfigureWarnings(b => b.Ignore(CosmosEventId.SyncNotSupported));
58+
```
59+
60+
Note, however, that we plan to fully remove sync support in EF11, so start updating to use async methods like `ToListAsync` and `SaveChangesAsync` as soon as possible!
61+
62+
### Enhanced primitive collections
63+
64+
> [!TIP]
65+
> The code shown here comes from [CosmosPrimitiveTypesSample.cs](https://github.com/dotnet/EntityFramework.Docs/tree/main/samples/core/Miscellaneous/NewInEFCore9.Cosmos/CosmosPrimitiveTypesSample.cs).
66+
67+
The Cosmos DB provider has supported primitive collections in a limited form since EF Core 6. This is support is being enhanced in EF9, starting with consolidation of the metadata and API surfaces for primitive collections in document databases to align with primitive collections in relational databases. This means that primitive collections can now be explicitly mapped using the model building API, allowing for facets of the element type to be configured. For example, to map a list of required (i.e. non-null) strings:
68+
69+
<!--
70+
modelBuilder.Entity<Book>()
71+
.PrimitiveCollection(e => e.Quotes)
72+
.ElementType(b => b.IsRequired());
73+
-->
74+
[!code-csharp[ConfigureCollection](../../../../samples/core/Miscellaneous/NewInEFCore9.Cosmos/CosmosPrimitiveTypesSample.cs?name=ConfigureCollection)]
75+
76+
See [What's new in EF8: primitive collections](xref:core/what-is-new/ef-core-8.0/whatsnew#primitive-collections) for more information on the model building API.
77+
78+
## AOT and pre-compiled queries
79+
80+
As mentioned in the introduction, there is a lot of work going on behind the scenes to allow EF Core to run without just-in-time (JIT) compilation. Instead, EF compile ahead-of-time (AOT) everything needed to run queries in the application. This AOT compilation and related processing will happen as part of building and publishing the application. At this point in the EF9 release, there is not much available that can be used by you, the app developer. However, for those interested, the completed issues in EF9 that support AOT and pre-compiled queries are:
81+
82+
- [Compiled model: Use static binding instead of reflection for properties and fields](https://github.com/dotnet/efcore/issues/24900)
83+
- [Compiled model: Generate lambdas used in change tracking](https://github.com/dotnet/efcore/issues/24904)
84+
- [Make change tracking and the update pipeline compatible with AOT/trimming](https://github.com/dotnet/efcore/issues/29761)
85+
- [Use interceptors to redirect the query to precompiled code](https://github.com/dotnet/efcore/issues/31331)
86+
- [Make all SQL expression nodes quotable](https://github.com/dotnet/efcore/issues/33008)
87+
- [Generate the compiled model during build](https://github.com/dotnet/efcore/issues/24894)
88+
- [Discover the compiled model automatically](https://github.com/dotnet/efcore/issues/24893)
89+
- [Make ParameterExtractingExpressionVisitor capable of extracting paths to evaluatable fragments in the tree](https://github.com/dotnet/efcore/issues/32999)
90+
- [Generate expression trees in compiled models (query filters, value converters)](https://github.com/dotnet/efcore/issues/29924)
91+
- [Make LinqToCSharpSyntaxTranslator more resilient to multiple declaration of the same variable in nested scopes](https://github.com/dotnet/efcore/issues/32716)
92+
- [Optimize ParameterExtractingExpressionVisitor](https://github.com/dotnet/efcore/issues/32698)
93+
94+
Check back here for examples of how to use pre-compiled queries as the experience comes together.
95+
2396
## LINQ and SQL translation
2497

2598
The team is working on some significant architecture changes to the query pipeline in EF Core 9 as part of our continued improvements to JSON mapping and document databases. This means we need to get **people like you** to run your code on these new internals. (If you're reading a "What's New" doc at this point in the release, then you're a really engaged part of the community; thank you!) We have over 120,000 tests, but it's not enough! We need you, people running real code on our bits, in order to find issues and ship a solid release!
2699

100+
<a name="groupby-complex-types"></a>
101+
102+
### GroupBy complex types
103+
104+
> [!TIP]
105+
> The code shown here comes from [ComplexTypesSample.cs](https://github.com/dotnet/EntityFramework.Docs/tree/main/samples/core/Miscellaneous/NewInEFCore9/ComplexTypesSample.cs).
106+
107+
EF9 supports grouping by a complex type instance. For example:
108+
109+
<!--
110+
var groupedAddresses = await context.Stores
111+
.GroupBy(b => b.StoreAddress)
112+
.Select(g => new { g.Key, Count = g.Count() })
113+
.ToListAsync();
114+
-->
115+
[!code-csharp[GroupByComplexType](../../../../samples/core/Miscellaneous/NewInEFCore9/ComplexTypesSample.cs?name=GroupByComplexType)]
116+
117+
EF translates this as grouping by each member of the complex type, which aligns with the semantics of complex types as value objects. For example, on Azure SQL:
118+
119+
```sql
120+
SELECT [s].[StoreAddress_City], [s].[StoreAddress_Country], [s].[StoreAddress_Line1], [s].[StoreAddress_Line2], [s].[StoreAddress_PostCode], COUNT(*) AS [Count]
121+
FROM [Stores] AS [s]
122+
GROUP BY [s].[StoreAddress_City], [s].[StoreAddress_Country], [s].[StoreAddress_Line1], [s].[StoreAddress_Line2], [s].[StoreAddress_PostCode]
123+
```
124+
27125
<a name="prune"></a>
28126

29127
### Prune columns passed to OPENJSON's WITH clause
@@ -680,6 +778,66 @@ Now, whenever the model changes, the compiled model will be automatically rebuil
680778
> [NOTE!]
681779
> We are working through some performance issues with changes made to the compiled model in EF8 and EF9. See [Issue 33483#](https://github.com/dotnet/efcore/issues/33483) for more information.
682780
781+
<a name="read-only-primitives"></a>
782+
783+
### Read-only primitive collections
784+
785+
> [!TIP]
786+
> The code shown here comes from [PrimitiveCollectionsSample.cs](https://github.com/dotnet/EntityFramework.Docs/tree/main/samples/core/Miscellaneous/NewInEFCore9/PrimitiveCollectionsSample.cs).
787+
788+
EF8 introduced support for [mapping arrays and mutable lists of primitive types](xref:core/what-is-new/ef-core-8.0/whatsnew#primitive-collections). This has been expanded in EF9 to include read-only collections/lists. Specifically, EF9 supports collections typed as `IReadOnlyList`, `IReadOnlyCollection`, or `ReadOnlyCollection`. For example, in the following code, `DaysVisited` will be mapped by convention as a primitive collection of dates:
789+
790+
```csharp
791+
public class DogWalk
792+
{
793+
public int Id { get; set; }
794+
public string Name { get; set; }
795+
public ReadOnlyCollection<DateOnly> DaysVisited { get; set; }
796+
}
797+
```
798+
799+
The read-only collection can be backed by a normal, mutable collection if desired. For example, in the following code, `DaysVisited` can be mapped as a primitive collection of dates, while still allowing code in the class to manipulate the underlying list.
800+
801+
```csharp
802+
public class Pub
803+
{
804+
public int Id { get; set; }
805+
public string Name { get; set; }
806+
public IReadOnlyCollection<string> Beers { get; set; }
807+
808+
private List<DateOnly> _daysVisited = new();
809+
public IReadOnlyList<DateOnly> DaysVisited => _daysVisited;
810+
}
811+
```
812+
813+
These collections can then be used in queries in the normal way. For example, this LINQ query:
814+
815+
<!--
816+
var walksWithADrink = await context.Walks.Select(
817+
w => new
818+
{
819+
WalkName = w.Name,
820+
PubName = w.ClosestPub.Name,
821+
Count = w.DaysVisited.Count(v => w.ClosestPub.DaysVisited.Contains(v)),
822+
TotalCount = w.DaysVisited.Count
823+
}).ToListAsync();
824+
-->
825+
[!code-csharp[WalksWithADrink](../../../../samples/core/Miscellaneous/NewInEFCore9/PrimitiveCollectionsSample.cs?name=WalksWithADrink)]
826+
827+
Which translates to the following SQL on SQLite:
828+
829+
```sql
830+
SELECT "w"."Name" AS "WalkName", "p"."Name" AS "PubName", (
831+
SELECT COUNT(*)
832+
FROM json_each("w"."DaysVisited") AS "d"
833+
WHERE "d"."value" IN (
834+
SELECT "d0"."value"
835+
FROM json_each("p"."DaysVisited") AS "d0"
836+
)) AS "Count", json_array_length("w"."DaysVisited") AS "TotalCount"
837+
FROM "Walks" AS "w"
838+
INNER JOIN "Pubs" AS "p" ON "w"."ClosestPubId" = "p"."Id"
839+
```
840+
683841
<a name="sequence-caching"></a>
684842

685843
### Specify caching for sequences
@@ -770,9 +928,6 @@ CREATE INDEX [IX_User_Name] ON [User] ([Name]) WITH (FILLFACTOR = 80);
770928
CREATE INDEX [IX_User_Region_Tag] ON [User] ([Region], [Tag]) WITH (FILLFACTOR = 80);
771929
```
772930

773-
> [!NOTE]
774-
> There is currently a bug in preview 2 where the fill-factors are not included when the table is created for the first time. This is tracked by [Issue #33269](https://github.com/dotnet/efcore/issues/33269)
775-
776931
This enhancement was contributed by [@deano-hunter](https://github.com/deano-hunter). Many thanks!
777932

778933
<a name="conventions"></a>

entity-framework/core/what-is-new/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,4 @@ See the [release planning process](xref:core/what-is-new/release-planning) for m
4848

4949
The next planned stable release is **EF Core 9.0**, or just **EF9**, scheduled for **November 2024**.
5050

51-
See the [high-level plan for EF9](xref:core/what-is-new/ef-core-9.0/plan) for more information.
51+
See the [What's New in EF9](xref:core/what-is-new/ef-core-9.0/whatsnew) for more information.

samples/core/Benchmarks/AverageBlogRanking.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public class BloggingContext : DbContext
8383
public DbSet<Blog> Blogs { get; set; }
8484

8585
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
86-
=> optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True");
86+
=> optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;ConnectRetryCount=0");
8787

8888
public void SeedData(int numblogs)
8989
{

samples/core/Benchmarks/CompiledQueries.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public class BloggingContext : DbContext
6262

6363
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
6464
=> optionsBuilder
65-
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True")
65+
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;ConnectRetryCount=0")
6666
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
6767

6868
public async Task SeedDataAsync(int numBlogs)

samples/core/Benchmarks/ContextPooling.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public class ContextPooling
1717
public void Setup()
1818
{
1919
_options = new DbContextOptionsBuilder<BloggingContext>()
20-
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True")
20+
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;ConnectRetryCount=0")
2121
.Options;
2222

2323
using var context = new BloggingContext(_options);

samples/core/Benchmarks/DynamicallyConstructedQueries.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ public class BloggingContext : DbContext
109109
public DbSet<Post> Posts { get; set; }
110110

111111
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
112-
=> optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True");
112+
=> optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;ConnectRetryCount=0");
113113
}
114114

115115
public class Blog

samples/core/Benchmarks/Inheritance.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public abstract class InheritanceContext : DbContext
7272
public DbSet<Root> Roots { get; set; }
7373

7474
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
75-
=> optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True");
75+
=> optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;ConnectRetryCount=0");
7676

7777
protected override void OnModelCreating(ModelBuilder modelBuilder)
7878
{

samples/core/Benchmarks/QueryTrackingBehavior.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public class BloggingContext : DbContext
4848
public DbSet<Post> Posts { get; set; }
4949

5050
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
51-
=> optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True");
51+
=> optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;ConnectRetryCount=0");
5252

5353
public static void SeedData(int numBlogs, int numPostsPerBlog)
5454
{
@@ -77,4 +77,4 @@ public class Post
7777
public int BlogId { get; set; }
7878
public Blog Blog { get; set; }
7979
}
80-
}
80+
}

samples/core/CascadeDeletes/IntroOptionalSamples.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,12 +159,12 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
159159
{
160160
optionsBuilder
161161
.EnableSensitiveDataLogging()
162-
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Scratch;Trusted_Connection=True");
162+
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Scratch;Trusted_Connection=True;ConnectRetryCount=0");
163163
//.UseSqlite("DataSource=test.db");
164164

165165
if (!_quiet)
166166
{
167167
optionsBuilder.LogTo(Console.WriteLine, new[] { RelationalEventId.CommandExecuted });
168168
}
169169
}
170-
}
170+
}

samples/core/CascadeDeletes/IntroRequiredSamples.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,12 +176,12 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
176176
{
177177
optionsBuilder
178178
.EnableSensitiveDataLogging()
179-
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Scratch;Trusted_Connection=True");
179+
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Scratch;Trusted_Connection=True;ConnectRetryCount=0");
180180
//.UseSqlite("DataSource=test.db");
181181

182182
if (!_quiet)
183183
{
184184
optionsBuilder.LogTo(Console.WriteLine, new[] { RelationalEventId.CommandExecuted });
185185
}
186186
}
187-
}
187+
}

0 commit comments

Comments
 (0)