diff --git a/entity-framework/core/cli/msbuild.md b/entity-framework/core/cli/msbuild.md index 7a537fa00b..7302fa5904 100644 --- a/entity-framework/core/cli/msbuild.md +++ b/entity-framework/core/cli/msbuild.md @@ -2,7 +2,7 @@ title: EF Core MSBuild tasks - EF Core description: Reference guide for the Entity Framework Core .NET MSBuild tasks author: AndriySvyryd -ms.date: 01/17/2025 +ms.date: 03/04/2026 uid: core/cli/msbuild --- @@ -30,7 +30,7 @@ If the project specifies `true` then by default the MSB | MSBuild property | Description | |--------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| EFOptimizeContext | Set to `true` to enable MSBuild integration. | +| EFOptimizeContext | Set to `true` to enable MSBuild integration. **EF Core 9-10 only; removed in EF Core 11.** | | EFScaffoldModelStage | Set to `publish`, `build` or `none` to indicate at which stage the compiled model will be generated. Defaults to `publish`. | | EFPrecompileQueriesStage | Set to `publish`, `build` or `none` to indicate at which stage the precompiled queries will be generated. Defaults to `publish`. | | DbContextName | The derived `DbContext` class to use. Class name only or fully qualified with namespaces. If this option is omitted, EF Core will perform generation for all context classes in the project. | @@ -38,6 +38,9 @@ If the project specifies `true` then by default the MSB | EFOutputDir | The folder to put the generated files before the project is compiled. If this option is omitted, EF Core will use `$(IntermediateOutputPath)`. | | EFNullable | Whether nullable reference types will be used in the generated code. If this option is omitted, EF Core will use `$(Nullable)`. | +> [!NOTE] +> Starting with EF Core 11, the `EFOptimizeContext` property has been [removed](xref:core/what-is-new/ef-core-11.0/breaking-changes#ef-optimize-context-removed). The `EFScaffoldModelStage` and `EFPrecompileQueriesStage` properties now work independently and don't require an additional enablement flag. + ## Limitations * When using the integration during the `publish` stage also set the rid in the startup project (e.g. \win-x64\) diff --git a/entity-framework/core/managing-schemas/migrations/projects.md b/entity-framework/core/managing-schemas/migrations/projects.md index a306bde830..657459747e 100644 --- a/entity-framework/core/managing-schemas/migrations/projects.md +++ b/entity-framework/core/managing-schemas/migrations/projects.md @@ -8,7 +8,7 @@ uid: core/managing-schemas/migrations/projects # Using a Separate Migrations Project -You may want to store your migrations in a different project than the one containing your `DbContext`. You can also use this strategy to maintain multiple sets of migrations, for example, one for development and another for release-to-release upgrades. +You may want to store your migrations in a different project than the one containing your `DbContext`. This is recommended if your project uses a platform-specific project type, such as WinUI, Xamarin, MAUI, Blazor, or Azure Functions, or if it targets a specific runtime identifier (RID). You can also use this strategy to maintain multiple sets of migrations, for example, one for development and another for release-to-release upgrades. > [!TIP] > You can view this article's [sample on GitHub](https://github.com/dotnet/EntityFramework.Docs/tree/main/samples/core/Schemas/ThreeProjectMigrations). @@ -59,3 +59,6 @@ Add-Migration NewMigration -Project WebApplication1.Migrations ``` *** + +> [!TIP] +> If your application uses dependency injection, consider implementing in your migrations project. This allows the EF tools to create your `DbContext` without needing to run the startup project. For more information, see [From a design-time factory](xref:core/cli/dbcontext-creation#from-a-design-time-factory). diff --git a/entity-framework/core/managing-schemas/migrations/teams.md b/entity-framework/core/managing-schemas/migrations/teams.md index 0a99ca8231..f11152ed8c 100644 --- a/entity-framework/core/managing-schemas/migrations/teams.md +++ b/entity-framework/core/managing-schemas/migrations/teams.md @@ -2,56 +2,33 @@ title: Migrations in Team Environments - EF Core description: Best practices for managing migrations and resolving conflicts in team environments with Entity Framework Core author: SamMonoRT -ms.date: 10/30/2017 +ms.date: 02/18/2026 uid: core/managing-schemas/migrations/teams --- # Migrations in Team Environments -When working with Migrations in team environments, pay extra attention to the model snapshot file. This file can tell you if your teammate's migration merges cleanly with yours or if you need to resolve a conflict by re-creating your -migration before sharing it. +When working with Migrations in team environments, various problems can arise when migrations are added by multiple developers around the same time; note that migrations aren't simply SQL scripts but also include a snapshot of the model at the time of that migration. -## Merging +For example, imagine developer A and B both create work branches at the same time, and generate a migration in their branches. If developer A merges their branch and then developer B does the same, the latest migration (developer B's) will have a context snapshot that does not include the changes from developer A's migration. This can cause various forms of corruption in later migrations. -When you merge migrations from your teammates, you may get conflicts in your model snapshot file. If both changes are unrelated, the merge is trivial and the two migrations can coexist. For example, you may get a merge conflict in the customer entity type configuration that looks like this: +As a result, it is highly recommended to coordinate in advance and to avoid working concurrently on migrations in multiple branches when possible. -```output -<<<<<<< Mine -b.Property("Deactivated"); -======= -b.Property("LoyaltyPoints"); ->>>>>>> Theirs -``` +## Detecting diverged migration trees -Since both of these properties need to exist in the final model, complete the merge by adding both properties. In many -cases, your version control system may automatically merge such changes for you. +> [!NOTE] +> This feature is being introduced in EF Core 11 from preview-3 onwards. -```csharp -b.Property("Deactivated"); -b.Property("LoyaltyPoints"); -``` +Starting with EF 11, the model snapshot records the ID of the latest migration. This means that if two developers each create a migration on separate branches, merging those branches will produce a source control conflict in the model snapshot file — since both branches modify the latest migration ID. This conflict is an important signal: it tells you that the migration trees have diverged, and one of them must be discarded before proceeding. -In these cases, your migration and your teammate's migration are independent of each other. Since either of them could be applied first, you don't need to make any additional changes to your migration before sharing it with your team. +To resolve this, follow the steps in [Resolving diverged migration trees](#resolving-diverged-migration-trees) below: abort the merge, remove your migration (keeping your model changes), merge your teammate's changes, and then re-add your migration. -## Resolving conflicts +## Resolving diverged migration trees -Sometimes you encounter a true conflict when merging the model snapshot model. For example, you and your teammate may each have renamed the same property. - -```output -<<<<<<< Mine -b.Property("Username"); -======= -b.Property("Alias"); ->>>>>>> Theirs -``` - -If you encounter this kind of conflict, resolve it by re-creating your migration. Follow these steps: +If, when merging a branch, a diverged migration tree is detected, resolve it by re-creating your migration. Follow these steps: 1. Abort the merge and rollback to your working directory before the merge 2. Remove your migration (but keep your model changes) 3. Merge your teammate's changes into your working directory 4. Re-add your migration -After doing this, the two migrations can be applied in the correct order. Their migration is applied first, renaming -the column to *Alias*, thereafter your migration renames it to *Username*. - -Your migration can safely be shared with the rest of the team. +After doing this, your migration is cleanly based on top of any migrations that have been added in the other branch, and its context snapshot contains all previous changes. Your migration can now be safely shared with the rest of the team. diff --git a/entity-framework/core/modeling/relationships/foreign-and-principal-keys.md b/entity-framework/core/modeling/relationships/foreign-and-principal-keys.md index 5ff361945a..0851e82de7 100644 --- a/entity-framework/core/modeling/relationships/foreign-and-principal-keys.md +++ b/entity-framework/core/modeling/relationships/foreign-and-principal-keys.md @@ -189,6 +189,32 @@ This can be changed in the model building API using `HasConstraintName`. For exa > [!TIP] > The constraint name is not used by the EF runtime. It is only used when creating a database schema using [EF Core Migrations](xref:core/managing-schemas/migrations/index). +### Excluding foreign key constraints from migrations + +> [!NOTE] +> This feature is being introduced in EF Core 11, which is currently in preview. + +Sometimes it is useful to have the foreign key relationship represented in the EF model, but without creating the corresponding foreign key constraint in the database. This can happen with legacy databases where constraints don't exist, or in data synchronization scenarios where the order of inserting related entities might temporarily violate referential integrity constraints. In these cases, use `ExcludeForeignKeyFromMigrations` to prevent EF from generating the foreign key constraint in migrations (and `EnsureCreated`): + +```csharp +modelBuilder.Entity() + .HasMany(e => e.Posts) + .WithOne(e => e.Blog) + .HasForeignKey(e => e.BlogId) + .ExcludeForeignKeyFromMigrations(); +``` + +With this configuration, EF will not create a foreign key constraint in the database, but the relationship is still tracked in the EF model and can be used normally for loading related data, change tracking, etc. EF will still create a database index for the foreign key column, since indexes benefit queries regardless of whether a constraint exists. + +To apply this across all foreign keys in the model (e.g. to globally disable all foreign key constraints), you can iterate over all foreign keys in `OnModelCreating`: + +```csharp +foreach (var foreignKey in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys())) +{ + foreignKey.SetIsExcludedFromMigrations(true); +} +``` + ### Indexes for foreign keys By convention, EF creates a database index for the property or properties of a foreign key. See [_Model building conventions_](xref:core/modeling/relationships/conventions) for more information about the types of indexes created by convention. diff --git a/entity-framework/core/providers/sql-server/full-text-search.md b/entity-framework/core/providers/sql-server/full-text-search.md index 54153a9fe7..abdba6496a 100644 --- a/entity-framework/core/providers/sql-server/full-text-search.md +++ b/entity-framework/core/providers/sql-server/full-text-search.md @@ -31,23 +31,23 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity
() .HasFullTextIndex(a => a.Contents) - .HasKeyIndex("PK_Articles") - .OnCatalog("ftCatalog"); + .UseKeyIndex("PK_Articles") + .UseCatalog("ftCatalog"); } ``` -The `HasKeyIndex()` method specifies the unique, non-nullable, single-column index used as the full-text key for the table (typically the primary key index). `OnCatalog()` assigns the full-text index to a specific catalog. +The `UseKeyIndex()` method specifies the unique, non-nullable, single-column index used as the full-text key for the table (typically the primary key index). `UseCatalog()` assigns the full-text index to a specific catalog. You can also configure multiple columns and additional options such as per-column languages and change tracking: ```csharp modelBuilder.Entity
() .HasFullTextIndex(a => new { a.Title, a.Contents }) - .HasKeyIndex("PK_Articles") - .OnCatalog("ftCatalog") - .WithChangeTracking(FullTextChangeTracking.Manual) - .HasLanguage("Title", "English") - .HasLanguage("Contents", "French"); + .UseKeyIndex("PK_Articles") + .UseCatalog("ftCatalog") + .HasChangeTracking(FullTextChangeTracking.Manual) + .UseLanguage("Title", "English") + .UseLanguage("Contents", "French"); ``` The full-text catalog can also be configured as the default catalog, and with accent sensitivity: @@ -185,7 +185,7 @@ The above automatically searches across all columns registered for full-text sea ```csharp var results = await context.Articles .Join( - context.Articles.FreeTextTable(a => a.Contents, "veggies"), + context.Articles.FreeTextTable("veggies", a => a.Contents), a => a.Id, ftt => ftt.Key, (a, ftt) => new { Article = a, ftt.Rank }) @@ -197,7 +197,7 @@ var results = await context.Articles ```csharp var results = await context.Articles - .FreeTextTable(a => new { a.Title, a.Contents }, "veggies") + .FreeTextTable("veggies", a => new { a.Title, a.Contents }) .Select(r => new { Article = r.Value, Rank = r.Rank }) .OrderByDescending(r => r.Rank) .ToListAsync(); @@ -224,7 +224,7 @@ Both table-valued functions support a `topN` parameter to limit the number of re ```csharp var results = await context.Articles - .FreeTextTable(a => a.Contents, "veggies", topN: 10) + .FreeTextTable("veggies", a => a.Contents, topN: 10) .Select(r => new { Article = r.Value, Rank = r.Rank }) .OrderByDescending(r => r.Rank) .ToListAsync(); @@ -236,7 +236,7 @@ Both table-valued functions support specifying a language term for linguistic ma ```csharp var results = await context.Articles - .FreeTextTable(a => a.Contents, "veggies", languageTerm: "English") + .FreeTextTable("veggies", a => a.Contents, languageTerm: "English") .Select(r => new { Article = r.Value, Rank = r.Rank }) .ToListAsync(); ``` diff --git a/entity-framework/core/providers/sql-server/functions.md b/entity-framework/core/providers/sql-server/functions.md index 5f5b801224..f7dae58129 100644 --- a/entity-framework/core/providers/sql-server/functions.md +++ b/entity-framework/core/providers/sql-server/functions.md @@ -232,6 +232,13 @@ stringValue.Trim() | LTRIM( stringValue.TrimEnd() | RTRIM(@stringValue) stringValue.TrimStart() | LTRIM(@stringValue) +## JSON functions + +.NET | SQL | Added in +----------------------------------------------------------------- | --------------------------------------------------------- | -------- +EF.Functions.JsonContains(json, searchValue, path?, searchMode?) | JSON_CONTAINS(@json, @searchValue, @path?, @searchMode?) | EF 11.0 +EF.Functions.JsonPathExists(json, path) | JSON_PATH_EXISTS(@json, @path) | EF 11.0 + ## Miscellaneous functions .NET | SQL | Added in diff --git a/entity-framework/core/providers/sql-server/vector-search.md b/entity-framework/core/providers/sql-server/vector-search.md index 29843fc23f..c075a0dad6 100644 --- a/entity-framework/core/providers/sql-server/vector-search.md +++ b/entity-framework/core/providers/sql-server/vector-search.md @@ -66,6 +66,9 @@ await context.SaveChangesAsync(); Once you have embeddings saved to your database, you're ready to perform vector similarity search over them. +> [!NOTE] +> Starting with EF Core 11, vector properties are not loaded by default when querying entities, since vectors are typically large and are rarely needed to be read back. Prior to EF Core 11, vector properties were always loaded like any other property. + ## Exact search with VECTOR_DISTANCE() The [`EF.Functions.VectorDistance()`](/sql/t-sql/functions/vector-distance-transact-sql) function computes the *exact* distance between two vectors. Use it to perform similarity search for a given user query: @@ -126,7 +129,9 @@ Once you have a vector index, use the `VectorSearch()` extension method on your ```csharp var blogs = await context.Blogs - .VectorSearch(b => b.Embedding, "cosine", embedding, topN: 5) + .VectorSearch(b => b.Embedding, embedding, "cosine") + .OrderBy(r => r.Distance) + .Take(5) .ToListAsync(); foreach (var (blog, score) in blogs) @@ -138,18 +143,26 @@ foreach (var (blog, score) in blogs) This translates to the following SQL: ```sql -SELECT [v].[Id], [v].[Embedding], [v].[Name] -FROM VECTOR_SEARCH([Blogs], 'Embedding', @__embedding, 'metric = cosine', @__topN) +SELECT TOP(@p) WITH APPROXIMATE [b].[Id], [b].[Name], [v].[Distance] +FROM VECTOR_SEARCH( + TABLE = [Blogs] AS [b], + COLUMN = [Embedding], + SIMILAR_TO = @p1, + METRIC = 'cosine' +) AS [v] +ORDER BY [v].[Distance] ``` -The `topN` parameter specifies the maximum number of results to return. +Compose `VectorSearch()` with `OrderBy(r => r.Distance)` and `Take(...)` to limit the number of results returned as required for approximate vector search. `VectorSearch()` returns `VectorSearchResult`, which allows you to access both the entity and the computed distance: ```csharp var searchResults = await context.Blogs - .VectorSearch(b => b.Embedding, "cosine", embedding, topN: 5) + .VectorSearch(b => b.Embedding, embedding, "cosine") .Where(r => r.Distance < 0.05) + .OrderBy(r => r.Distance) + .Take(5) .Select(r => new { Blog = r.Value, Distance = r.Distance }) .ToListAsync(); ``` @@ -172,7 +185,10 @@ var results = await context.Articles .FreeTextTable(textualQuery, topN: k) // Perform vector (semantic) search, joining the results of both searches together .LeftJoin( - context.Articles.VectorSearch(b => b.Embedding, queryEmbedding, "cosine", topN: k), + context.Articles + .VectorSearch(b => b.Embedding, queryEmbedding, "cosine") + .OrderBy(r => r.Distance) + .Take(k), fts => fts.Key, vs => vs.Value.Id, (fts, vs) => new @@ -206,14 +222,17 @@ This query: The query produces the following SQL: ```sql -SELECT TOP(@p3) [a0].[Id], [a0].[Content], [a0].[Embedding], [a0].[Title] +SELECT TOP(@p3) [a0].[Id], [a0].[Content], [a0].[Title] FROM FREETEXTTABLE([Articles], *, @p, @p1) AS [f] -LEFT JOIN VECTOR_SEARCH( - TABLE = [Articles] AS [a0], - COLUMN = [Embedding], - SIMILAR_TO = @p2, - METRIC = 'cosine', - TOP_N = @p3 -) AS [v] ON [f].[KEY] = [a0].[Id] -ORDER BY 1.0E0 / CAST(10 + [f].[RANK] AS float) + ISNULL(1.0E0 / (10.0E0 + [v].[Distance]), 0.0E0) DESC +LEFT JOIN ( + SELECT TOP(@p4) WITH APPROXIMATE [a0].[Id], [a0].[Content], [a0].[Title], [v].[Distance] + FROM VECTOR_SEARCH( + TABLE = [Articles] AS [a0], + COLUMN = [Embedding], + SIMILAR_TO = @p2, + METRIC = 'cosine' + ) AS [v] + ORDER BY [v].[Distance] +) AS [v0] ON [f].[KEY] = [v0].[Id] +ORDER BY 1.0E0 / CAST(10 + [f].[RANK] AS float) + ISNULL(1.0E0 / (10.0E0 + [v0].[Distance]), 0.0E0) DESC ``` diff --git a/entity-framework/core/what-is-new/ef-core-11.0/breaking-changes.md b/entity-framework/core/what-is-new/ef-core-11.0/breaking-changes.md index 6b21720370..0df2589c36 100644 --- a/entity-framework/core/what-is-new/ef-core-11.0/breaking-changes.md +++ b/entity-framework/core/what-is-new/ef-core-11.0/breaking-changes.md @@ -19,27 +19,227 @@ This page documents API and behavior changes that have the potential to break ex | **Breaking change** | **Impact** | |:--------------------------------------------------------------------------------------------------------------- | -----------| | [Sync I/O via the Azure Cosmos DB provider has been fully removed](#cosmos-nosync) | Medium | +| [Microsoft.Data.SqlClient has been updated to 7.0](#sqlclient-7) | Medium | +| [EF Core now throws by default when no migrations are found](#migrations-not-found) | Low | +| [`EFOptimizeContext` MSBuild property has been removed](#ef-optimize-context-removed) | Low | +| [EF tools packages no longer reference Microsoft.EntityFrameworkCore.Design](#ef-tools-no-design-dep) | Low | +| [SqlVector properties are no longer loaded by default](#sqlvector-not-auto-loaded) | Low | +| [Cosmos: empty owned collections now return an empty collection instead of null](#cosmos-empty-collections) | Low | -### Medium-impact changes +## Medium-impact changes -#### Sync I/O via the Azure Cosmos DB provider has been fully removed +### Sync I/O via the Azure Cosmos DB provider has been fully removed [Tracking Issue #37059](https://github.com/dotnet/efcore/issues/37059) -##### Old behavior +#### Old behavior Synchronous I/O via the Azure Cosmos DB provider has been unsupported since EF 9.0 ([note](/ef/core/what-is-new/ef-core-9.0/breaking-changes#cosmos-nosync)); calling any sync I/O API - like `ToList` or `SaveChanges` threw an exception, unless a special opt-in was configured. When the opt-in was configured, sync I/O APIs worked as before, causing the provider to perform "sync-over-async" blocking against the Azure Cosmos DB SDK, which could result in deadlocks and other performance issues. -##### New behavior +#### New behavior Starting with EF Core 11.0, EF now always throws when a synchronous I/O API is called. There is no way to opt back into using sync I/O APIs. -##### Why +#### Why Synchronous blocking on asynchronous methods ("sync-over-async") is highly discouraged, and can lead to deadlock and other performance problems. Since the Azure Cosmos DB SDK only supports async methods, so does the EF Cosmos provider. -##### Mitigations +#### Mitigations Convert your code to use async I/O APIs instead of sync I/O ones. For example, replace calls to `SaveChanges()` with `await SaveChangesAsync()`. + + + +### Microsoft.Data.SqlClient has been updated to 7.0 + +#### Old behavior + +EF Core 10 used [Microsoft.Data.SqlClient](https://www.nuget.org/packages/Microsoft.Data.SqlClient/) 6.x, which included Azure/Entra ID authentication dependencies (such as `Azure.Core`, `Azure.Identity`, and `Microsoft.Identity.Client`) in the core package. + +#### New behavior + +EF Core 11 now depends on [Microsoft.Data.SqlClient](https://www.nuget.org/packages/Microsoft.Data.SqlClient/) 7.0. This version removes Azure/Entra ID (formerly Azure Active Directory) authentication dependencies from the core package. If your application uses Entra ID authentication (for example, `ActiveDirectoryDefault`, `ActiveDirectoryInteractive`, `ActiveDirectoryManagedIdentity`, or `ActiveDirectoryServicePrincipal`), you must now install the [`Microsoft.Data.SqlClient.Extensions.Azure`](https://www.nuget.org/packages/Microsoft.Data.SqlClient.Extensions.Azure/) package separately. + +In addition, `SqlAuthenticationMethod.ActiveDirectoryPassword` has been marked as obsolete. + +For more details, see the [Microsoft.Data.SqlClient 7.0 release notes](https://github.com/dotnet/SqlClient/blob/main/release-notes/7.0/7.0.0.md). + +#### Why + +This change was made in [Microsoft.Data.SqlClient](https://www.nuget.org/packages/Microsoft.Data.SqlClient/) to reduce dependency bloat for applications that don't use Azure authentication, which is especially beneficial for containerized deployments and local development. + +#### Mitigations + +If your application uses Entra ID authentication with SQL Server, add a reference to the `Microsoft.Data.SqlClient.Extensions.Azure` package in your project: + +```xml + +``` + +No code changes are required beyond adding this package reference. If you use `SqlAuthenticationMethod.ActiveDirectoryPassword`, migrate to a modern authentication method such as `ActiveDirectoryDefault` or `ActiveDirectoryInteractive`. + +## Low-impact changes + + + +### EF Core now throws by default when no migrations are found + +[Tracking Issue #35218](https://github.com/dotnet/efcore/issues/35218) + +#### Old behavior + +Previously, when calling or on a database with no migrations in the assembly, EF Core logged an informational message and returned without applying any changes. + +#### New behavior + +Starting with EF Core 11.0, EF Core throws an exception by default when no migrations are found in the assembly. This is consistent with the `PendingModelChangesWarning` behavior [introduced in EF 9.0](xref:core/what-is-new/ef-core-9.0/breaking-changes#pending-model-changes). + +#### Why + +Calling `Migrate()` or `MigrateAsync()` when no migrations exist typically indicates a misconfiguration. Rather than silently continuing and leaving the database in a potentially incorrect state, EF Core now alerts developers to this issue immediately. + +#### Mitigations + +If you intentionally call `Migrate()` without having any migrations (for example, because you manage the database schema through other means), remove the `Migrate()` call or suppress the exception by configuring warnings: + +```csharp +options.ConfigureWarnings(w => w.Ignore(RelationalEventId.MigrationsNotFound)) +``` + +Or to log the event instead of throwing: + +```csharp +options.ConfigureWarnings(w => w.Log(RelationalEventId.MigrationsNotFound)) +``` + + + +### `EFOptimizeContext` MSBuild property has been removed + +[Tracking Issue #35079](https://github.com/dotnet/efcore/issues/35079) + +#### Old behavior + +Previously, the `EFOptimizeContext` MSBuild property could be set to `true` to enable compiled model and precompiled query code generation during build or publish: + +```xml +true +``` + +#### New behavior + +Starting with EF Core 11.0, the `EFOptimizeContext` MSBuild property has been removed. Code generation is now controlled exclusively through the `EFScaffoldModelStage` and `EFPrecompileQueriesStage` properties. When `PublishAOT` is set to `true`, code generation is automatically enabled during publish without needing any additional property. + +#### Why + +The `EFScaffoldModelStage` and `EFPrecompileQueriesStage` properties already provide fine-grained control over when code generation occurs. `EFOptimizeContext` was a redundant enablement gate. + +#### Mitigations + +Replace usages of `EFOptimizeContext` with the `EFScaffoldModelStage` and `EFPrecompileQueriesStage` properties. These can be set to `publish` or `build` to control at which stage code generation occurs: + +```xml +publish +publish +``` + +Any other value (for example, `none`) disables the corresponding generation. + +If you have `PublishAOT` set to `true`, code generation is automatically enabled during publish and no additional configuration is needed. + + + +### EF tools packages no longer reference Microsoft.EntityFrameworkCore.Design + +[Tracking Issue #37739](https://github.com/dotnet/efcore/issues/37739) + +#### Old behavior + +Previously, the `Microsoft.EntityFrameworkCore.Tools` and `Microsoft.EntityFrameworkCore.Tasks` NuGet packages had a dependency on `Microsoft.EntityFrameworkCore.Design`. + +#### New behavior + +Starting with EF Core 11.0, the `Microsoft.EntityFrameworkCore.Tools` and `Microsoft.EntityFrameworkCore.Tasks` NuGet packages no longer have a dependency on `Microsoft.EntityFrameworkCore.Design`. + +#### Why + +There was no hard dependency on the code in `Microsoft.EntityFrameworkCore.Design`, and this dependency was causing issues when using the latest `Microsoft.EntityFrameworkCore.Tools` with projects targeting older frameworks. + +#### Mitigations + +If your project relies on `Microsoft.EntityFrameworkCore.Design` being brought in transitively through the tools packages, add a direct reference to it in your project: + +```xml + +``` + + + +### SqlVector properties are no longer loaded by default + +[Tracking Issue #37279](https://github.com/dotnet/efcore/issues/37279) + +#### Old behavior + +Previously, when querying entities with `SqlVector` properties, EF Core included the vector column in `SELECT` statements and populated the property on the returned entity. + +#### New behavior + +Starting with EF Core 11.0, `SqlVector` properties are no longer included in `SELECT` statements when materializing entities. The property will be `null` on returned entities. + +Vector properties can still be used in `WHERE` and `ORDER BY` clauses—including with `VectorDistance()` and `VectorSearch()`; they just won't be included in the entity projection. + +#### Why + +Vector columns can be very large, containing hundreds or thousands of floating-point values. In the vast majority of cases, vectors are written to the database and then used for search, without needing to be read back. Excluding them from `SELECT` by default avoids unnecessary data transfer. + +#### Mitigations + +> [!NOTE] +> A mechanism for opting vector properties back into automatic loading will be introduced later in the EF Core 11 release. + +If you need to read back vector values, use an explicit projection: + +```csharp +var embeddings = await context.Blogs + .Select(b => new { b.Id, b.Embedding }) + .ToListAsync(); +``` + + + +### Cosmos: empty owned collections now return an empty collection instead of null + +[Tracking Issue #36577](https://github.com/dotnet/efcore/issues/36577) + +#### Old behavior + +Previously, when querying entities via the Azure Cosmos DB provider where an owned collection contained no items, the collection property was `null` on the materialized entity. + +#### New behavior + +Starting with EF Core 11.0, the Azure Cosmos DB provider correctly initializes empty owned collections, returning an empty collection instead of `null`. + +#### Why + +The previous behavior of materializing empty owned collections as `null` was a bug. + +#### Mitigations + +If your code explicitly checks owned collection properties for `null` to detect that the collection is empty, those checks can simply be removed, since the collection is now always initialized: + +```csharp +// Before +if (entity.OwnedCollection is null or { Count: 0 }) +{ + // treated as empty +} + +// After +if (entity.OwnedCollection is { Count: 0 }) +{ + // treated as empty +} +``` diff --git a/entity-framework/core/what-is-new/ef-core-11.0/provider-facing-changes.md b/entity-framework/core/what-is-new/ef-core-11.0/provider-facing-changes.md index 34972144d9..719b378cec 100644 --- a/entity-framework/core/what-is-new/ef-core-11.0/provider-facing-changes.md +++ b/entity-framework/core/what-is-new/ef-core-11.0/provider-facing-changes.md @@ -2,7 +2,7 @@ title: Provider-facing changes in EF Core 11 (EF11) - EF Core description: List of provider-facing changes introduced in Entity Framework Core 11 (EF11) author: roji -ms.date: 01/16/2026 +ms.date: 04/08/2026 uid: core/what-is-new/ef-core-11.0/provider-facing-changes --- @@ -13,7 +13,9 @@ This page documents noteworthy changes in EF Core 11 which may affect EF provide ## Changes * Collation names are now quoted in SQL, like column and table names ([see #37462](https://github.com/dotnet/efcore/issues/37462)). If your database doesn't support collation name quoting, override `QuerySqlGenerator.VisitSql()` and `MigrationsSqlGenerator.ColumnDefinition()` to revert to the previous behavior, but it's recommended to implement some sort of restricted character validation. +* The `JsonPath` property on `IColumnModification` and `ColumnModificationParameters` has changed from `string?` to the new structured `JsonPath` type ([PR #38038](https://github.com/dotnet/efcore/pull/38038)). The `JsonPath` class provides `Segments`, `Ordinals`, an `IsRoot` property, and an `AppendTo(StringBuilder)` method for rendering the JSONPATH string. Providers that override `UpdateSqlGenerator.AppendUpdateColumnValue()` or otherwise handle JSON partial updates should update their code to use this new type. Where previously you checked for `null` or `"$"`, use `JsonPath is not { IsRoot: false }` instead, and call `JsonPath.AppendTo(stringBuilder)` to write the JSONPATH string representation. ## Test changes * The inheritance specification tests have been reorganized into a folder of their own ([PR](https://github.com/dotnet/efcore/pull/37410)). +* Query test classes now support non-shared-model tests via a new `QueryFixtureBase` class that all query fixtures extend ([PR #37681](https://github.com/dotnet/efcore/pull/37681)). The previous pattern of extending `SharedStoreFixtureBase` and `IQueryFixtureBase` separately has been replaced. `NonSharedPrimitiveCollectionsQueryTestBase` has been merged into `PrimitiveCollectionsQueryTestBase`, and per-type array tests have been moved to `TypeTestBase.Primitive_collection_in_query`. diff --git a/entity-framework/core/what-is-new/ef-core-11.0/whatsnew.md b/entity-framework/core/what-is-new/ef-core-11.0/whatsnew.md index a6c8aa1b4c..9e0c7577af 100644 --- a/entity-framework/core/what-is-new/ef-core-11.0/whatsnew.md +++ b/entity-framework/core/what-is-new/ef-core-11.0/whatsnew.md @@ -77,153 +77,133 @@ This enhancement removes a significant limitation when modeling complex domain h For more information on inheritance mapping strategies, see [Inheritance](xref:core/modeling/inheritance). + + +### Stabilization and bug fixes + +Significant effort has gone into making sure that complex type support is stable and bug-free, to unblock using complex types as an alternative to the owned entity mapping approach. Bugs fixed include: + +* [Error querying on complex type whose container is mapped to a table and a view](https://github.com/dotnet/efcore/issues/34706) +* [Problem with ComplexProperty in EF9, when using the TPT approach](https://github.com/dotnet/efcore/issues/35392) +* [Comparison of complex types does not compare properties within nested complex types](https://github.com/dotnet/efcore/issues/37391) +* [Assignment of complex type does not assign nested properties correctly (ExecuteUpdate)](https://github.com/dotnet/efcore/issues/37395) +* [Map two classes with same nullable complex properties to same column → NullReferenceException](https://github.com/dotnet/efcore/issues/37335) +* [Complex property stored as JSON marked non-nullable in TPH class hierarchy](https://github.com/dotnet/efcore/issues/37404) +* [EntityEntry.ReloadAsync throws when nullable complex property is null](https://github.com/dotnet/efcore/issues/37559) +* [Unnecessary columns in SQL with Complex Types + object closures in projections](https://github.com/dotnet/efcore/issues/37551) + ## LINQ and SQL translation - + -### MaxBy and MinBy +### Better SQL for to-one joins -EF Core now supports translating the LINQ `MaxByAsync` and `MinByAsync` methods (and their sync counterparts). These methods allow you to find the element with the maximum or minimum value for a given key selector, rather than just the maximum or minimum value itself. +EF Core 11 generates better SQL when querying with to-one (reference) navigation includes in two ways. -For example, to find the blog with the most posts: +First, when using split queries (`AsSplitQuery()`), EF previously added unnecessary joins to reference navigations in the SQL generated for collection queries. For example, consider the following query: ```csharp -var blogWithMostPosts = await context.Blogs.MaxByAsync(b => b.Posts.Count()); +var blogs = context.Blogs + .Include(b => b.BlogType) + .Include(b => b.Posts) + .AsSplitQuery() + .ToList(); ``` -This translates to the following SQL: +EF Core previously generated a split query for `Posts` that unnecessarily joined `BlogType`: ```sql -SELECT TOP(1) [b].[Id], [b].[Name] +-- Before EF Core 11 +SELECT [p].[Id], [p].[BlogId], [p].[Title], [b].[Id], [b0].[Id] FROM [Blogs] AS [b] -ORDER BY ( - SELECT COUNT(*) - FROM [Posts] AS [p] - WHERE [b].[Id] = [p].[BlogId]) DESC +INNER JOIN [BlogType] AS [b0] ON [b].[BlogTypeId] = [b0].[Id] +INNER JOIN [Post] AS [p] ON [b].[Id] = [p].[BlogId] +ORDER BY [b].[Id], [b0].[Id] ``` -Similarly, `MinByAsync` orders ascending and returns the element with the minimum value for the key selector. - -## Cosmos DB - - - -### Transactional batches - -Azure Cosmos DB supports [transactional batches](/azure/cosmos-db/transactional-batch), which allow multiple operations to be executed atomically and in a single roundtrip against a single partition. Starting with EF Core 11, the provider leverages transactional batches by default, providing best-effort atomicity and improved performance when saving changes. - -The batching behavior can be controlled via the property: - -* **Auto** (default): Operations are grouped into transactional batches by container and partition. Batches are executed sequentially; if a batch fails, subsequent batches are not executed. -* **Never**: All operations are performed individually and sequentially (the pre-11 behavior). -* **Always**: Requires all operations to fit in a single transactional batch; throws if they cannot. - -For more information, [see the documentation](xref:core/providers/cosmos/saving#transactionality-and-transactional-batches). - -This feature was contributed by [@JoasE](https://github.com/JoasE) - many thanks! - - - -### Bulk execution - -Azure Cosmos DB supports _bulk execution_, which allows multiple document operations to be executed in parallel and across DbContext instances, significantly improving throughput when saving many entities at once. EF Core now supports enabling bulk execution: +In EF Core 11, the unneeded join is pruned: -```csharp -protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - => optionsBuilder.UseCosmos( - "", - databaseName: "OrdersDB", - options => options.BulkExecutionEnabled()); +```sql +-- EF Core 11 +SELECT [p].[Id], [p].[BlogId], [p].[Title], [b].[Id] +FROM [Blogs] AS [b] +INNER JOIN [Post] AS [p] ON [b].[Id] = [p].[BlogId] +ORDER BY [b].[Id] ``` -For more information, [see the documentation](xref:core/providers/cosmos/saving#bulk-execution). - -This feature was contributed by [@JoasE](https://github.com/JoasE) - many thanks! - - - -### Session token management - -Azure Cosmos DB uses session tokens to track read-your-writes consistency within a session. When running in an environment with multiple instances (e.g., with round-robin load balancing), you may need to manually manage session tokens to ensure consistency across requests. - -EF Core now provides APIs to retrieve and set session tokens on a `DbContext`. To enable manual session token management, configure the `SessionTokenManagementMode()`: +Second, EF no longer adds redundant keys from reference navigations to `ORDER BY` clauses. Because a reference navigation's key is functionally determined by the parent entity's key (via the foreign key), it does not need to appear separately. For example: ```csharp -protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - => optionsBuilder.UseCosmos( - "", - databaseName: "OrdersDB", - options => options.SessionTokenManagementMode(SessionTokenManagementMode.SemiAutomatic)); +var blogs = context.Blogs + .Include(b => b.Owner) + .Include(b => b.Posts) + .ToList(); ``` -You can then retrieve and use session tokens: - -```csharp -// After performing operations, retrieve the session token -var sessionToken = context.Database.GetSessionToken(); +EF Core previously included `[p].[PersonId]` in the `ORDER BY`, even though `[b].[BlogId]` already uniquely identifies the row: -// Later, in a different context instance, apply the session token before reading -context.Database.UseSessionToken(sessionToken); -var result = await context.Documents.FindAsync(id); +```sql +-- Before EF Core 11 +ORDER BY [b].[BlogId], [p].[PersonId] ``` -For more information, [see the documentation](xref:core/providers/cosmos/saving#session-token-management). +In EF Core 11, the redundant column is omitted: -This feature was contributed by [@JoasE](https://github.com/JoasE) - many thanks! - -## Migrations +```sql +-- EF Core 11 +ORDER BY [b].[BlogId] +``` - +Both optimizations can have a significant positive impact on query performance, especially when multiple reference navigations are included. -### Create and apply migrations in one step + -The `dotnet ef database update` command now supports creating and applying a migration in a single step using the new `--add` option. This uses Roslyn to compile the migration at runtime, enabling scenarios like .NET Aspire and containerized applications where recompilation isn't possible: +### MaxBy and MinBy -```dotnetcli -dotnet ef database update InitialCreate --add -``` +EF Core now supports translating the LINQ `MaxByAsync` and `MinByAsync` methods (and their sync counterparts). These methods allow you to find the element with the maximum or minimum value for a given key selector, rather than just the maximum or minimum value itself. -This command scaffolds a new migration named `InitialCreate`, compiles it using Roslyn, and immediately applies it to the database. The migration files are still saved to disk for source control and future recompilation. The same options available for `dotnet ef migrations add` can be used: +For example, to find the blog with the most posts: -```dotnetcli -dotnet ef database update AddProducts --add --output-dir Migrations/Products --namespace MyApp.Migrations +```csharp +var blogWithMostPosts = await context.Blogs.MaxByAsync(b => b.Posts.Count()); ``` -In PowerShell, use the `-Add` parameter: +This translates to the following SQL: -```powershell -Update-Database -Migration InitialCreate -Add +```sql +SELECT TOP(1) [b].[Id], [b].[Name] +FROM [Blogs] AS [b] +ORDER BY ( + SELECT COUNT(*) + FROM [Posts] AS [p] + WHERE [b].[Id] = [p].[BlogId]) DESC ``` - +Similarly, `MinByAsync` orders ascending and returns the element with the minimum value for the key selector. -### Connection and offline options for migrations remove +### EF.Functions.JsonPathExists() -The `dotnet ef migrations remove` and `database drop` commands now accept `--connection` parameters, allowing you to specify the database connection string directly without needing to configure a default connection in your `DbContext`. Additionally, `migrations remove` supports the new `--offline` option to remove a migration without connecting to the database: +EF Core 11 introduces `EF.Functions.JsonPathExists()`, which checks whether a given JSON path exists in a JSON document. On SQL Server, this translates to the [`JSON_PATH_EXISTS`](/sql/t-sql/functions/json-path-exists-transact-sql) function (available since SQL Server 2022). -```console -# Remove migration with specific connection -dotnet ef migrations remove --connection "Server=prod;Database=MyDb;..." +The following query filters blogs to those whose JSON data contains an `OptionalInt` property: -# Remove migration without connecting to database (offline mode) -dotnet ef migrations remove --offline +```csharp +var blogs = await context.Blogs + .Where(b => EF.Functions.JsonPathExists(b.JsonData, "$.OptionalInt")) + .ToListAsync(); +``` -# Revert and remove applied migration -dotnet ef migrations remove --force +This generates the following SQL: -# Drop specific database by connection string -dotnet ef database drop --connection "Server=test;Database=MyDb;..." --force +```sql +SELECT [b].[Id], [b].[Name], [b].[JsonData] +FROM [Blogs] AS [b] +WHERE JSON_PATH_EXISTS([b].[JsonData], N'$.OptionalInt') = 1 ``` -The `--offline` option skips the database connection check entirely, which is useful when the database is inaccessible or when you're certain the migration hasn't been applied. Note that `--offline` and `--force` cannot be used together, since `--force` requires a database connection to check if the migration has been applied before reverting it. +`EF.Functions.JsonPathExists()` accepts a JSON value and a JSON path to check for. It can be used with scalar string properties, complex types, and owned entity types mapped to JSON columns. -In PowerShell, use the `-Connection` and `-Offline` parameters: - -```powershell -Remove-Migration -Connection "Server=prod;Database=MyDb;..." -Remove-Migration -Offline -Drop-Database -Connection "Server=test;Database=MyDb;..." -Force -``` +For the full `JSON_PATH_EXISTS` SQL Server documentation, see [`JSON_PATH_EXISTS`](/sql/t-sql/functions/json-path-exists-transact-sql). ## SQL Server @@ -250,16 +230,37 @@ Once you have a vector index, you can use the `VectorSearch()` extension method ```csharp var blogs = await context.Blogs - .VectorSearch(b => b.Embedding, "cosine", embedding, topN: 5) + .VectorSearch(b => b.Embedding, embedding, "cosine") + .OrderBy(r => r.Distance) + .Take(5) .ToListAsync(); ``` -This translates to the SQL Server [`VECTOR_SEARCH()`](/sql/t-sql/functions/vector-search-transact-sql) table-valued function, which performs an approximate search over the vector index. The `topN` parameter specifies the number of results to return. +This translates to the SQL Server [`VECTOR_SEARCH()`](/sql/t-sql/functions/vector-search-transact-sql) table-valued function, which performs an approximate search over the vector index. Compose with `OrderBy(r => r.Distance)` and `Take(...)` to limit the number of results returned. `VectorSearch()` returns `VectorSearchResult`, allowing you to access the distance alongside the entity. For more information, see the [full documentation on vector search](xref:core/providers/sql-server/vector-search). + + +### Vector properties not loaded by default + +EF Core 11 changes how vector properties are loaded: `SqlVector` columns are no longer included in `SELECT` statements when materializing entities. Since vectors can be quite large—containing hundreds or thousands of floating-point numbers—this avoids unnecessary data transfer in the common case where vectors are ingested and used for search but not read back. + +```csharp +// Vector column is excluded from the projected entity +var blogs = await context.Blogs.OrderBy(b => b.Name).ToListAsync(); +// Generates: SELECT [b].[Id], [b].[Name] FROM [Blogs] AS [b] ... + +// Explicit projection still loads the vector +var embeddings = await context.Blogs + .Select(b => new { b.Id, b.Embedding }) + .ToListAsync(); +``` + +Vector properties can still be used in `WHERE` and `ORDER BY` clauses—including with `VectorDistance()` and `VectorSearch()`—and EF will correctly include them in the SQL, just not in the entity projection. + ### Full-text search improvements @@ -274,8 +275,8 @@ modelBuilder.HasFullTextCatalog("ftCatalog"); modelBuilder.Entity() .HasFullTextIndex(b => b.FullName) - .HasKeyIndex("PK_Blogs") - .OnCatalog("ftCatalog"); + .UseKeyIndex("PK_Blogs") + .UseCatalog("ftCatalog"); ``` This generates the following SQL in a migration: @@ -287,6 +288,8 @@ CREATE FULLTEXT INDEX ON [Blogs]([FullName]) KEY INDEX [PK_Blogs] ON [ftCatalog] Previously, full-text catalog and index creation had to be managed manually by adding SQL to migrations. For full details on setting up full-text catalogs and indexes, see the [full-text search documentation](xref:core/providers/sql-server/full-text-search#setting-up-full-text-search). + + #### Full-text search table-valued functions EF Core has long provided support for SQL Server's full-text search predicates `FREETEXT()` and `CONTAINS()`, via `EF.Functions.FreeText()` and `EF.Functions.Contains()`. These predicates can be used in LINQ `Where()` clauses to filter results based on search criteria. @@ -296,14 +299,14 @@ However, SQL Server also has table-valued function versions of these functions, ```csharp // Using FreeTextTable with a search query var results = await context.Blogs - .FreeTextTable(b => b.FullName, "John") + .FreeTextTable("John", b => b.FullName) .Select(r => new { Blog = r.Value, Rank = r.Rank }) .OrderByDescending(r => r.Rank) .ToListAsync(); // Using ContainsTable with a search query var results = await context.Blogs - .ContainsTable(b => b.FullName, "John") + .ContainsTable("John", b => b.FullName) .Select(r => new { Blog = r.Value, Rank = r.Rank }) .OrderByDescending(r => r.Rank) .ToListAsync(); @@ -315,7 +318,7 @@ For more information, see the [full documentation on full-text search](xref:core -### Translate Contains over primitive collections using JSON_CONTAINS +### Contains operations using JSON_CONTAINS SQL Server 2025 introduced the [`JSON_CONTAINS`](/sql/t-sql/functions/json-contains-transact-sql) function, which checks whether a value exists in a JSON document. Starting with EF Core 11, when targeting SQL Server 2025, LINQ `Contains` queries over primitive (or scalar) collections stored as JSON are translated to use this function, replacing the previous, less efficient `OPENJSON`-based translation. @@ -358,3 +361,180 @@ WHERE JSON_CONTAINS([b].[Tags], 'ef-core') = 1 > [!NOTE] > `JSON_CONTAINS` does not support searching for null values. As a result, this translation is only applied when EF can determine that at least one side is non-nullable — either the item being searched for (e.g. a non-null constant or a non-nullable column), or the collection's elements. When this cannot be determined, EF falls back to the previous `OPENJSON`-based translation. + +#### EF.Functions.JsonContains() + +In the section above, EF Core automatically translates LINQ `Contains` queries over primitive collections to use the SQL Server `JSON_CONTAINS` function. In some cases, however, you may want to directly invoke `JSON_CONTAINS` yourself, for example to search for a value at a specific path, or to specify a search mode. For these cases, EF Core 11 introduces `EF.Functions.JsonContains()`. + +The following query checks whether a blog's JSON data contains a specific value at a given path: + +```csharp +var blogs = await context.Blogs + .Where(b => EF.Functions.JsonContains(b.JsonData, 8, "$.Rating") == 1) + .ToListAsync(); +``` + +This generates the following SQL: + +```sql +SELECT [b].[Id], [b].[Name], [b].[JsonData] +FROM [Blogs] AS [b] +WHERE JSON_CONTAINS([b].[JsonData], 8, N'$.Rating') = 1 +``` + +`EF.Functions.JsonContains()` accepts the JSON value to search in, the value to search for, and optionally a JSON path and a search mode. It can be used with scalar string properties, complex types, and owned entity types mapped to JSON columns. + +For the full `JSON_CONTAINS` SQL Server documentation, see [`JSON_CONTAINS`](/sql/t-sql/functions/json-contains-transact-sql). + +## Cosmos DB + + + +### Transactional batches + +Azure Cosmos DB supports [transactional batches](/azure/cosmos-db/transactional-batch), which allow multiple operations to be executed atomically and in a single roundtrip against a single partition. Starting with EF Core 11, the provider leverages transactional batches by default, providing best-effort atomicity and improved performance when saving changes. + +The batching behavior can be controlled via the property: + +* **Auto** (default): Operations are grouped into transactional batches by container and partition. Batches are executed sequentially; if a batch fails, subsequent batches are not executed. +* **Never**: All operations are performed individually and sequentially (the pre-11 behavior). +* **Always**: Requires all operations to fit in a single transactional batch; throws if they cannot. + +For more information, [see the documentation](xref:core/providers/cosmos/saving#transactionality-and-transactional-batches). + +This feature was contributed by [@JoasE](https://github.com/JoasE) - many thanks! + + + +### Bulk execution + +Azure Cosmos DB supports _bulk execution_, which allows multiple document operations to be executed in parallel and across DbContext instances, significantly improving throughput when saving many entities at once. EF Core now supports enabling bulk execution: + +```csharp +protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + => optionsBuilder.UseCosmos( + "", + databaseName: "OrdersDB", + options => options.BulkExecutionEnabled()); +``` + +For more information, [see the documentation](xref:core/providers/cosmos/saving#bulk-execution). + +This feature was contributed by [@JoasE](https://github.com/JoasE) - many thanks! + + + +### Session token management + +Azure Cosmos DB uses session tokens to track read-your-writes consistency within a session. When running in an environment with multiple instances (e.g., with round-robin load balancing), you may need to manually manage session tokens to ensure consistency across requests. + +EF Core now provides APIs to retrieve and set session tokens on a `DbContext`. To enable manual session token management, configure the `SessionTokenManagementMode()`: + +```csharp +protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + => optionsBuilder.UseCosmos( + "", + databaseName: "OrdersDB", + options => options.SessionTokenManagementMode(SessionTokenManagementMode.SemiAutomatic)); +``` + +You can then retrieve and use session tokens: + +```csharp +// After performing operations, retrieve the session token +var sessionToken = context.Database.GetSessionToken(); + +// Later, in a different context instance, apply the session token before reading +context.Database.UseSessionToken(sessionToken); +var result = await context.Documents.FindAsync(id); +``` + +For more information, [see the documentation](xref:core/providers/cosmos/saving#session-token-management). + +This feature was contributed by [@JoasE](https://github.com/JoasE) - many thanks! + +## Migrations + + + +### Excluding foreign key constraints from migrations + +It is now possible to configure a foreign key relationship in the EF model while preventing the corresponding database constraint from being created by migrations. This is useful for legacy databases without existing constraints, or in data synchronization scenarios where referential integrity constraints might conflict with the synchronization order: + +```csharp +modelBuilder.Entity() + .HasMany(e => e.Posts) + .WithOne(e => e.Blog) + .HasForeignKey(e => e.BlogId) + .ExcludeForeignKeyFromMigrations(); +``` + +The relationship is fully supported in EF for queries, change tracking, etc. Only the foreign key constraint in the database is suppressed; a database index is still created on the foreign key column. + +For more information, see [Excluding foreign key constraints from migrations](xref:core/modeling/relationships/foreign-and-principal-keys#excluding-foreign-key-constraints-from-migrations). + + + +### Latest migration ID recorded in model snapshot + +When working in team environments, it's common for multiple developers to create migrations on separate branches. When these branches are merged, the migration trees can diverge, leading to issues that are sometimes difficult to detect. + +Starting with EF Core 11, the model snapshot now records the ID of the latest migration. When two developers create migrations on divergent branches, both branches will modify this value in the model snapshot, causing a source control merge conflict. This conflict alerts the team that they need to resolve the divergence - typically by discarding one of the migration trees and creating a new, unified migration. + +For more information on managing migrations in team environments, see [Migrations in Team Environments](xref:core/managing-schemas/migrations/teams). + + + +### Create and apply migrations in one step + +The `dotnet ef database update` command now supports creating and applying a migration in a single step using the new `--add` option. This uses Roslyn to compile the migration at runtime, enabling scenarios like .NET Aspire and containerized applications where recompilation isn't possible: + +```dotnetcli +dotnet ef database update InitialCreate --add +``` + +This command scaffolds a new migration named `InitialCreate`, compiles it using Roslyn, and immediately applies it to the database. The migration files are still saved to disk for source control and future recompilation. The same options available for `dotnet ef migrations add` can be used: + +```dotnetcli +dotnet ef database update AddProducts --add --output-dir Migrations/Products --namespace MyApp.Migrations +``` + +In PowerShell, use the `-Add` parameter: + +```powershell +Update-Database -Migration InitialCreate -Add +``` + + + +### Connection and offline options for migrations remove + +The `dotnet ef migrations remove` and `database drop` commands now accept `--connection` parameters, allowing you to specify the database connection string directly without needing to configure a default connection in your `DbContext`. Additionally, `migrations remove` supports the new `--offline` option to remove a migration without connecting to the database: + +```console +# Remove migration with specific connection +dotnet ef migrations remove --connection "Server=prod;Database=MyDb;..." + +# Remove migration without connecting to database (offline mode) +dotnet ef migrations remove --offline + +# Revert and remove applied migration +dotnet ef migrations remove --force + +# Drop specific database by connection string +dotnet ef database drop --connection "Server=test;Database=MyDb;..." --force +``` + +The `--offline` option skips the database connection check entirely, which is useful when the database is inaccessible or when you're certain the migration hasn't been applied. Note that `--offline` and `--force` cannot be used together, since `--force` requires a database connection to check if the migration has been applied before reverting it. + +In PowerShell, use the `-Connection` and `-Offline` parameters: + +```powershell +Remove-Migration -Connection "Server=prod;Database=MyDb;..." +Remove-Migration -Offline +Drop-Database -Connection "Server=test;Database=MyDb;..." -Force +``` + +## Other improvements + +* The EF command-line tool now writes all logging and status messages to standard error, reserving standard output only for the command's actual expected output. For example, when generating a migration SQL script with `dotnet ef migrations script`, only the SQL is written to standard output. diff --git a/entity-framework/core/what-is-new/ef-core-9.0/breaking-changes.md b/entity-framework/core/what-is-new/ef-core-9.0/breaking-changes.md index 862f0f5397..886e9d2e48 100644 --- a/entity-framework/core/what-is-new/ef-core-9.0/breaking-changes.md +++ b/entity-framework/core/what-is-new/ef-core-9.0/breaking-changes.md @@ -33,6 +33,7 @@ EF Core 9 targets .NET 8. This means that existing applications that target .NET | [SqlFunctionExpression's nullability arguments' arity validated](#sqlfunctionexpression-nullability) | Low | | [`ToString()` method now returns empty string for `null` instances](#nullable-tostring) | Low | | [Shared framework dependencies were updated to 9.0.x](#shared-framework-dependencies) | Low | +| [EF tools no longer support .NET Framework projects](#ef-tools-no-netfx) | Low | ## High-impact changes @@ -333,6 +334,28 @@ The matching dependency versions contain the latest security fixes and using the Change your app to target net9.0 to get the previous behavior. + + +### EF tools no longer support .NET Framework projects + +[Tracking Issue #37745](https://github.com/dotnet/efcore/issues/37745) + +#### Old behavior + +Previously, the EF Core tools (`dotnet-ef` CLI and Package Manager Console tools) worked with projects targeting .NET Framework. + +#### New behavior + +Starting with EF Core 9.0, the EF Core tools no longer work with projects targeting .NET Framework. The tools produce an error when the startup project targets .NET Framework. + +#### Why + +The current version of EF Core tools works with all supported EF Core versions and there are no longer any supported EF Core versions that work on .NET Framework. + +#### Mitigations + +Update your project to target .NET (e.g., .NET 8 or later). If your project currently targets .NET Framework, see the [porting guide](/dotnet/core/porting/) for information on migrating to .NET. + ## Azure Cosmos DB breaking changes Extensive work has gone into making the Azure Cosmos DB provider better in 9.0. The changes include a number of high-impact breaking changes; if you are upgrading an existing application, please read the following carefully.