diff --git a/entity-framework/core/cli/dotnet.md b/entity-framework/core/cli/dotnet.md index 1f0e66d35c..3217b723fc 100644 --- a/entity-framework/core/cli/dotnet.md +++ b/entity-framework/core/cli/dotnet.md @@ -152,7 +152,10 @@ Options: | Option | Description | |:------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------| -| `--connection ` | The connection string to the database. Defaults to the one specified in `AddDbContext` or `OnConfiguring`. | +| `--connection ` | The connection string to the database. Defaults to the one specified in `AddDbContext` or `OnConfiguring`. | +| `--add` | Creates a new migration and applies it to the database in a single step. Uses Roslyn to compile the migration at runtime. When specified, the `` argument is required and provides the name for the new migration. Added in EF Core 11. | +| `--output-dir ` | The directory to put migration files in. Paths are relative to the target project directory. Requires `--add`. Added in EF Core 11. | +| `--namespace ` | The namespace to use for the generated migration classes. Requires `--add`. Added in EF Core 11. | The [common options](#common-options) are listed above. @@ -163,6 +166,13 @@ dotnet ef database update InitialCreate dotnet ef database update 20180904195021_InitialCreate --connection your_connection_string ``` +The following examples create a new migration and apply it to the database in one step: + +```dotnetcli +dotnet ef database update InitialCreate --add +dotnet ef database update AddProducts --add --output-dir Migrations/Products --namespace MyApp.Migrations +``` + ## `dotnet ef dbcontext info` Gets information about a `DbContext` type. diff --git a/entity-framework/core/cli/powershell.md b/entity-framework/core/cli/powershell.md index 8868bcd962..307c71e0a3 100644 --- a/entity-framework/core/cli/powershell.md +++ b/entity-framework/core/cli/powershell.md @@ -316,6 +316,9 @@ Updates the database to the last migration or to a specified migration. |:------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `-Migration ` | The target migration. Migrations may be identified by name or by ID. The number 0 is a special case that means *before the first migration* and causes all migrations to be reverted. If no migration is specified, the command defaults to the last migration. | | `-Connection ` | The connection string to the database. Defaults to the one specified in `AddDbContext` or `OnConfiguring`. | +| `-Add` | Creates a new migration and applies it to the database in a single step. Uses Roslyn to compile the migration at runtime. When specified, a migration name is required and provides the name for the new migration. Added in EF Core 11. | +| `-OutputDir ` | The directory to put migration files in. Paths are relative to the target project directory. Requires `-Add`. Added in EF Core 11. | +| `-Namespace ` | The namespace to use for the generated migration classes. Requires `-Add`. Added in EF Core 11. | The [common parameters](#common-parameters) are listed above. @@ -335,6 +338,12 @@ Update-Database InitialCreate Update-Database 20180904195021_InitialCreate -Connection your_connection_string ``` +The following example creates a new migration and applies it to the database in one step: + +```powershell +Update-Database -Migration InitialCreate -Add +``` + ## Additional resources * [Migrations](xref:core/managing-schemas/migrations/index) diff --git a/entity-framework/core/managing-schemas/migrations/managing.md b/entity-framework/core/managing-schemas/migrations/managing.md index 2f903799bd..574999e5d3 100644 --- a/entity-framework/core/managing-schemas/migrations/managing.md +++ b/entity-framework/core/managing-schemas/migrations/managing.md @@ -65,6 +65,37 @@ Add-Migration InitialCreate -OutputDir Your\Directory *** +## Create and apply a migration in one step + +> [!NOTE] +> This feature was added in EF Core 11. + +The `dotnet ef database update` command supports creating and applying a migration in a single step using the `--add` option. This uses Roslyn to compile the migration at runtime, enabling scenarios like .NET Aspire and containerized applications where the application cannot be stopped and rebuilt: + +### [.NET CLI](#tab/dotnet-core-cli) + +```dotnetcli +dotnet ef database update InitialCreate --add +``` + +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 +``` + +### [Visual Studio](#tab/vs) + +```powershell +Update-Database -Migration InitialCreate -Add +``` + +*** + +This command scaffolds a new migration with the specified name, 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. + +If no pending model changes are detected, the command applies any existing pending migrations without creating a new one. + ## Customize migration code While EF Core generally creates accurate migrations, you should always review the code and make sure it corresponds to the desired change; in some cases, it is even necessary to do so. diff --git a/entity-framework/core/modeling/indexes.md b/entity-framework/core/modeling/indexes.md index 0760817b0d..854c942bc9 100644 --- a/entity-framework/core/modeling/indexes.md +++ b/entity-framework/core/modeling/indexes.md @@ -2,7 +2,7 @@ title: Indexes - EF Core description: Configuring indexes in an Entity Framework Core model author: SamMonoRT -ms.date: 10/1/2021 +ms.date: 06/09/2026 uid: core/modeling/indexes --- # Indexes @@ -40,6 +40,58 @@ An index can also span more than one column: Indexes over multiple columns, also known as *composite indexes*, speed up queries which filter on index's columns, but also queries which only filter on the *first* columns covered by the index. See the [performance docs](xref:core/performance/efficient-querying#use-indexes-properly) for more information. +## Indexes on complex type properties + +Starting with EF Core 11.0, indexes can use scalar properties nested inside [complex types](xref:core/what-is-new/ef-core-10.0/whatsnew#complex-types): + +```csharp +modelBuilder.Entity() + .HasIndex(c => c.Address.PostalCode); +``` + +Composite indexes can mix regular entity properties and complex type properties: + +```csharp +modelBuilder.Entity() + .HasIndex(c => new { c.Region, c.Address.PostalCode }); +``` + +The same paths can be specified by name: + +```csharp +modelBuilder.Entity() + .HasIndex("Address.PostalCode"); +``` + +For complex types mapped to JSON columns, providers may also support indexing paths inside the JSON document: + +```csharp +modelBuilder.Entity() + .ComplexProperty(c => c.Address, b => b.ToJson()); + +modelBuilder.Entity() + .HasIndex("Address.PostalCode"); +``` + +Indexes over complex collections use collection path syntax. `[]` represents all elements, while `[0]`, `[1]`, and so on represent a specific element: + +```csharp +modelBuilder.Entity() + .ComplexCollection(o => o.Items, b => b.ToJson()); + +modelBuilder.Entity() + .HasIndex("Items[].Sku"); +``` + +The same index can be configured with a lambda expression using `Select`: + +```csharp +modelBuilder.Entity() + .HasIndex(o => o.Items.Select(i => i.Sku)); +``` + +Support for JSON-path indexes depends on the database provider. For example, the SQL Server provider creates SQL Server JSON indexes where supported, while the Azure Cosmos DB provider emits the configured paths into the container indexing policy. + ## Index uniqueness By default, indexes aren't unique: multiple rows are allowed to have the same value(s) for the index's column set. You can make an index unique as follows: @@ -148,6 +200,14 @@ In the following example, the `Url` column is part of the index key, so any quer [!code-csharp[Main](../../../samples/core/Modeling/IndexesAndConstraints/FluentAPI/IndexInclude.cs?name=IndexInclude&highlight=5-9)] +Starting with EF Core 11.0, SQL Server included columns can also refer to scalar properties nested inside complex types: + +```csharp +modelBuilder.Entity() + .HasIndex(c => c.Name) + .IncludeProperties(c => c.Address.City); +``` + ## Check constraints Check constraints are a standard relational feature that allows you to define a condition that must hold for all rows in a table; any attempt to insert or modify data that violates the constraint will fail. Check constraints are similar to non-null constraints (which prohibit nulls in a column) or to unique constraints (which prohibit duplicates), but allow arbitrary SQL expression to be defined. diff --git a/entity-framework/core/modeling/keys.md b/entity-framework/core/modeling/keys.md index 50c27ac7c2..caa8a83768 100644 --- a/entity-framework/core/modeling/keys.md +++ b/entity-framework/core/modeling/keys.md @@ -2,7 +2,7 @@ title: Keys - EF Core description: How to configure keys for entity types when using Entity Framework Core author: AndriySvyryd -ms.date: 10/14/2022 +ms.date: 06/09/2026 uid: core/modeling/keys --- # Keys @@ -53,6 +53,24 @@ public class Car *** +## Keys on complex type properties + +Starting with EF Core 11.0, keys can use scalar properties nested inside non-collection [complex types](xref:core/what-is-new/ef-core-10.0/whatsnew#complex-types). + +```csharp +modelBuilder.Entity() + .HasKey(c => c.CustomerId.Value); +``` + +The same path can be specified by name: + +```csharp +modelBuilder.Entity() + .HasKey("CustomerId.Value"); +``` + +Complex properties on the path to a key property are required. Keys can't traverse complex collections or nullable complex properties. + ## Value generation For non-composite numeric and GUID primary keys, EF Core sets up value generation for you by convention. For example, a numeric primary key in SQL Server is automatically set up to be an IDENTITY column. For more information, see [the documentation on value generation](xref:core/modeling/generated-properties) and [guidance for specific inheritance mapping strategies](xref:core/modeling/inheritance#key-generation). diff --git a/entity-framework/core/providers/cosmos/modeling.md b/entity-framework/core/providers/cosmos/modeling.md index 50ec61e101..dae31de9c7 100644 --- a/entity-framework/core/providers/cosmos/modeling.md +++ b/entity-framework/core/providers/cosmos/modeling.md @@ -2,7 +2,7 @@ title: Modeling - Azure Cosmos DB Provider - EF Core description: Configuring the model with the Azure Cosmos DB EF Core Provider author: SamMonoRT -ms.date: 09/26/2024 +ms.date: 06/09/2026 uid: core/providers/cosmos/modeling --- # Configuring the model with the EF Core Azure Cosmos DB Provider @@ -67,6 +67,47 @@ If you don't configure a partition key with EF, a warning will be logged at star Once your partition key properties are properly configured, you can provide values for them in queries; see [Querying with partition keys](xref:core/providers/cosmos/querying#partition-keys) for more information. +## Indexing policy + +Starting with EF Core 11.0, the Azure Cosmos DB provider emits more of the EF model's index configuration into the container [indexing policy](/azure/cosmos-db/index-policy). + +By default, Azure Cosmos DB automatically indexes all properties. You can configure this automatic indexing policy and exclude individual paths: + +```csharp +modelBuilder.Entity() + .HasAutomaticIndexing() + .Except("/InternalNotes/?"); +``` + +To index only explicitly configured paths, configure indexes with `HasIndex`. Automatic indexing is disabled automatically as soon as any single-property index is defined, so there is no need to call `HasAutomaticIndexing(false)` explicitly: + +```csharp +modelBuilder.Entity(b => +{ + b.HasIndex(o => o.OrderNumber); + b.HasIndex(o => new { o.CustomerId, o.OrderDate }); +}); +``` + +When automatic indexing is enabled, single-property indexes are already covered by the default `/*` included path and aren't emitted separately. Composite, vector, and full-text indexes are always emitted. + +Indexes can also traverse complex type properties and complex collections. In a string path, `[]` represents all elements of a collection: + +```csharp +modelBuilder.Entity(b => +{ + b.ComplexCollection(o => o.Items); + b.HasIndex("Items[].Sku"); +}); +``` + +The same index can be configured with a lambda expression: + +```csharp +modelBuilder.Entity() + .HasIndex(o => o.Items.Select(i => i.Sku)); +``` + ## Discriminators Since multiple entity types may be mapped to the same container, EF Core always adds a `$type` discriminator property to all JSON documents you save (this property was called `Discriminator` before EF 9.0); this allows EF to recognize documents being loaded from the database, and materialize the right .NET type. Developers coming from relational databases may be familiar with discriminators in the context of [table-per-hierarchy inheritance (TPH)](xref:core/modeling/inheritance#table-per-hierarchy-and-discriminator-configuration); in Azure Cosmos DB, discriminators are used not just in inheritance mapping scenarios, but also because the same container can contain completely different document types. diff --git a/entity-framework/core/providers/sql-server/index.md b/entity-framework/core/providers/sql-server/index.md index 56d3210ba0..5512f5f984 100644 --- a/entity-framework/core/providers/sql-server/index.md +++ b/entity-framework/core/providers/sql-server/index.md @@ -2,7 +2,7 @@ title: Microsoft SQL Server Database Provider - EF Core description: Documentation for the database provider that allows Entity Framework Core to be used with Microsoft SQL Server author: AndriySvyryd -ms.date: 11/15/2021 +ms.date: 06/09/2026 uid: core/providers/sql-server/index --- # Microsoft SQL Server EF Core Database Provider @@ -113,6 +113,36 @@ To configure EF with a compatibility level, use `UseCompatibilityLevel()` as fol optionsBuilder.UseSqlServer("", o => o.UseCompatibilityLevel(170)); ``` +## JSON indexes + +Starting with EF Core 11.0, the SQL Server provider can create and scaffold [SQL Server JSON indexes](/sql/t-sql/statements/create-json-index-transact-sql) for paths inside complex types mapped to JSON columns. + +For example, the following maps a complex type to a JSON column and creates a JSON index over a nested property: + +```csharp +modelBuilder.Entity() + .ComplexProperty(c => c.Contact, b => b.ToJson().HasColumnType("json")); + +modelBuilder.Entity() + .HasIndex("Contact.Address.City"); +``` + +When SQL Server JSON indexes are supported, migrations generate SQL similar to: + +```sql +CREATE JSON INDEX [IX_Customers_Contact_Address_City] +ON [Customers]([Contact]) FOR (N'$.Address.City'); +``` + +Multiple paths inside the same JSON column can be indexed together: + +```csharp +modelBuilder.Entity() + .HasIndex(["Contact.Address.City", "Contact.PhoneNumber"]); +``` + +Paths through JSON arrays can use numeric indexers, for example `Orders[0].Number`. SQL Server JSON indexes don't support wildcard indexes over every array element. + ## Connection resiliency EF includes functionality for automatically retrying failed database commands; for more information, [see the documentation](xref:core/miscellaneous/connection-resiliency). When using and , connection resiliency is automatically set up with the appropriate settings specific for those databases. Otherwise, when using , configure the provider with as shown in the connection resiliency documentation. diff --git a/entity-framework/core/providers/sql-server/vector-search.md b/entity-framework/core/providers/sql-server/vector-search.md index 6a47ca7b05..370b7614db 100644 --- a/entity-framework/core/providers/sql-server/vector-search.md +++ b/entity-framework/core/providers/sql-server/vector-search.md @@ -196,25 +196,31 @@ SqlVector queryEmbedding = ...; var results = await context.Articles // Perform full-text search .FreeTextTable(textualQuery, topN: k) + .Join( + context.Articles, + fts => fts.Key, + a => a.Id, + (fts, a) => new { Article = a, fts.Rank }) // Perform vector (semantic) search, joining the results of both searches together - .LeftJoin( + .FullJoin( context.Articles.VectorSearch(b => b.Embedding, queryEmbedding, "cosine") .OrderBy(r => r.Distance) .Take(k) .WithApproximate(), - fts => fts.Key, + fts => fts.Article.Id, vs => vs.Value.Id, (fts, vs) => new { - Article = vs.Value, - FullTextRank = fts.Rank, - VectorDistance = (double?)vs.Distance + Article = fts != null ? fts.Article : vs.Value, + FullTextRank = fts == null ? null : (int?)fts.Rank, + VectorDistance = vs == null ? null : (double?)vs.Distance }) // Apply Reciprocal Rank Fusion (RRF) to combine the results .Select(x => new { x.Article, - RrfScore = (1.0 / (k + x.FullTextRank)) + (1.0 / (k + x.VectorDistance) ?? 0.0) + RrfScore = (x.FullTextRank == null ? 0.0 : 1.0 / (k + x.FullTextRank.Value)) + + (x.VectorDistance == null ? 0.0 : 1.0 / (k + x.VectorDistance.Value)) }) .OrderByDescending(x => x.RrfScore) .Take(10) @@ -225,19 +231,17 @@ var results = await context.Articles This query: 1. Performs a full-text search on `Article` -2. Performs a vector search on `Article` and combines the results to the full-text search results via a LEFT JOIN +2. Performs a vector search on `Article` and combines the results with the full-text search results via a FULL JOIN 3. Calculates the RRF score by combining both the full text and the semantic ranking 4. Orders by RRF score, takes the desired number of results and projects out the original `Article` entities. -> [!NOTE] -> Rather than using a LEFT JOIN, a FULL OUTER JOIN would be more suitable for this scenario; this would allow highly-ranking results from either search side to be included in the final result, even if that result does not appear at all on the other side. With the above LEFT JOIN approach, if a result has a very high vector similarity score, it never gets included in the final result if that result doesn't also have a high full-text score. However, EF doesn't currently support FULL OUTER JOIN; upvote [#37633](https://github.com/dotnet/efcore/issues/37633) if this is something you'd like to see supported. - The query produces the following SQL: ```sql -SELECT TOP(@__p_4) [a0].[Id], [a0].[Content], [a0].[Title] +SELECT TOP(@__p_4) COALESCE([a].[Id], [t].[Id]) AS [Id], COALESCE([a].[Content], [t].[Content]) AS [Content], COALESCE([a].[Title], [t].[Title]) AS [Title] FROM FREETEXTTABLE([Articles], *, @__textualQuery_0, @__k_1) AS [f] -LEFT JOIN ( +INNER JOIN [Articles] AS [a] ON [f].[KEY] = [a].[Id] +FULL JOIN ( SELECT TOP(@__k_1) WITH APPROXIMATE [a].[Id], [a].[Content], [a].[Title], [v].[Distance] FROM VECTOR_SEARCH( TABLE = [Articles] AS [a], @@ -246,6 +250,6 @@ LEFT JOIN ( METRIC = 'cosine' ) AS [v] ORDER BY [v].[Distance] -) AS [t] ON [f].[KEY] = [t].[Id] -ORDER BY 1.0E0 / CAST(@__k_1 + [f].[RANK] AS float) + ISNULL(1.0E0 / (CAST(@__k_1 AS float) + [t].[Distance]), 0.0E0) DESC +) AS [t] ON [a].[Id] = [t].[Id] +ORDER BY ISNULL(1.0E0 / CAST(@__k_1 + [f].[RANK] AS float), 0.0E0) + ISNULL(1.0E0 / (CAST(@__k_1 AS float) + [t].[Distance]), 0.0E0) DESC ``` diff --git a/entity-framework/core/providers/sqlite/functions.md b/entity-framework/core/providers/sqlite/functions.md index bcc0c1e3a4..6725219b68 100644 --- a/entity-framework/core/providers/sqlite/functions.md +++ b/entity-framework/core/providers/sqlite/functions.md @@ -22,7 +22,9 @@ group.Min(x => x.Property) | MIN(Property) group.Sum(x => x.Property) | SUM(Property) group.Sum(x => x.DecimalProperty) | ef_sum(DecimalProperty) | EF Core 9.0 string.Concat(group.Select(x => x.Property)) | group_concat(Property, '') +string.Concat(group.OrderBy(x => x.Other).Select(x => x.Property)) | group_concat(Property, '' ORDER BY Other) | EF Core 11.0 string.Join(separator, group.Select(x => x.Property)) | group_concat(Property, @separator) +string.Join(separator, group.OrderBy(x => x.Other).Select(x => x.Property)) | group_concat(Property, @separator ORDER BY Other) | EF Core 11.0 ## Binary functions 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 d47791ef7d..fb4fd80bca 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 @@ -2,7 +2,7 @@ title: Breaking changes in EF Core 11 (EF11) - EF Core description: List of breaking changes introduced in Entity Framework Core 11 (EF11) author: SamMonoRT -ms.date: 03/27/2026 +ms.date: 06/30/2026 uid: core/what-is-new/ef-core-11.0/breaking-changes --- @@ -30,6 +30,7 @@ This page documents API and behavior changes that have the potential to break ex | [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 | +| [Owned JSON collections without an explicit key are obsolete](#owned-json-collections-obsolete) | Low | ## Medium-impact changes @@ -121,7 +122,6 @@ The previous escaping scheme was non-injective: the escape character `^` was nev If your application uses composite keys whose values can contain the characters `/`, `\`, `?`, or `#`, be aware of the following: - **Existing data**: Documents previously stored in Cosmos DB have `id` values using the old escape sequences (e.g. `Post|1|^2F`). After upgrading to EF Core 11, EF will generate unescaped `id` values (e.g. `Post|1|/`) and will no longer find those existing documents. To continue accessing existing data without migration, opt back into the old behavior using the `AppContext` switch described above—however, be aware that the id-collision bug will still be present. - - **New data**: If you are creating a new application or database, avoid using these illegal characters in key values, as they are not valid in Cosmos DB resource `id` values. See the [Azure documentation](xref:Microsoft.Azure.Documents.Resource.Id) for details. ## Low-impact changes @@ -321,6 +321,78 @@ if (entity.OwnedCollection is { Count: 0 }) } ``` + + +### Owned JSON collections without an explicit key are obsolete + +[Tracking Issue #37289](https://github.com/dotnet/efcore/issues/37289) + +#### Old behavior + +Previously, owned entity types mapped to a JSON column via could be used as collections without configuring an explicit primary key. EF Core would synthesize an ordinal (positional) key behind the scenes to identify each item in the collection: + +```csharp +public class Blog +{ + public int Id { get; set; } + public List Posts { get; set; } = new(); +} + +public class Post +{ + // No key property + public required string Title { get; set; } + public required string Content { get; set; } +} + +protected override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder.Entity().OwnsMany(b => b.Posts, b => b.ToJson()); +``` + +#### New behavior + +Starting with EF Core 11.0, configuring an owned JSON collection without an explicit key produces an `OwnedEntityMappedToJsonCollectionWarning` warning. The mapping continues to work, but is now considered deprecated and is expected to be removed in a future release. + +Owned JSON entities that have an explicit primary key, as well as non-collection owned JSON references, are not affected by this change. + +#### Why + +[Complex types](xref:core/what-is-new/ef-core-10.0/whatsnew#complex-types) became fully supported in EF Core 10, including for [JSON mapping](xref:core/what-is-new/ef-core-10.0/whatsnew#json). Complex types are a better fit than owned types for JSON documents: they have value semantics and no identity, which avoids many of the issues that come from using owned entity types—which are entity types—to model what is fundamentally a value embedded in another document. In particular, owned JSON collections without an explicit key relied on a synthetic ordinal key, which has known limitations and corner cases. + +#### Mitigations + +The recommended mitigation is to migrate the type to a complex type, which is now the preferred way to map types to JSON: + +```csharp +protected override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder.Entity().ComplexCollection(b => b.Posts, b => b.ToJson()); +``` + +Alternatively, if you need to keep the owned-type mapping, configure a non-shadow primary key on the owned type. Once a key is configured, the warning no longer applies: + +```csharp +public class Post +{ + public int Id { get; set; } + public required string Title { get; set; } + public required string Content { get; set; } +} + +protected override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder.Entity().OwnsMany(b => b.Posts, b => + { + b.ToJson(); + b.HasKey(p => p.Id); + }); +``` + +If you cannot migrate immediately, you can suppress the warning via `ConfigureWarnings`: + +```csharp +protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + => optionsBuilder.ConfigureWarnings(w => w.Ignore(CoreEventId.OwnedEntityMappedToJsonCollectionWarning)); +``` + ## Microsoft.Data.Sqlite breaking changes @@ -334,6 +406,7 @@ if (entity.OwnedCollection is { Count: 0 }) |:----------------------------------------------------------------------------------------------------------|------------| | [Encryption-enabled SQLite packages have been removed](#sqlite-encryption-removed) | Medium | | [Some SQLitePCLRaw bundle packages have been removed](#sqlite-bundles-removed) | Medium | +| [Microsoft.Data.Sqlite now bundles SQLite3 Multiple Ciphers](#sqlite3mc) | Low | ### Medium-impact changes @@ -359,10 +432,10 @@ The previous no-cost `SQLitePCLRaw.bundle_e_sqlcipher` package was barely mainta If you need SQLite encryption, you have the following options: +- **SQLite3 Multiple Ciphers**: Starting with Microsoft.Data.Sqlite 11.0, the default SQLite build supports encryption and can be configured to use SQLCipher-compatible encryption. See [Microsoft.Data.Sqlite now bundles SQLite3 Multiple Ciphers](#sqlite3mc). NuGet packages are also available from [SQLite3MultipleCiphers-NuGet](https://github.com/utelle/SQLite3MultipleCiphers-NuGet). + - When encrypting a new database or opening an existing database that was encrypted with SQLCipher, you must configure the cipher scheme in the connection string using URI parameters—for example: `Data Source=file:example.db?cipher=sqlcipher&legacy=4;Password=`. See [How to open an existing database encrypted with SQLCipher](https://github.com/utelle/SQLite3MultipleCiphers-NuGet#how-to-open-an-existing-database-encrypted-with-sqlcipher) for details. - **SQLite Encryption Extension (SEE)**: This is the official encryption implementation from the SQLite team. A paid license is required. See [https://sqlite.org/com/see.html](https://sqlite.org/com/see.html) for details. NuGet packages are available through [SourceGear's SQLite build service](https://github.com/ericsink/SQLitePCL.raw/wiki/SQLite-encryption-options-for-use-with-SQLitePCLRaw). - **SQLCipher**: Purchase supported builds from [Zetetic](https://www.zetetic.net/sqlcipher/), or build the [open source code](https://github.com/sqlcipher/sqlcipher) yourself. -- **SQLite3 Multiple Ciphers**: NuGet packages are available from [SQLite3MultipleCiphers-NuGet](https://github.com/utelle/SQLite3MultipleCiphers-NuGet). - - When encrypting a new database or opening an existing database that was encrypted with SQLCipher, you must configure the cipher scheme in the connection string using URI parameters—for example: `Data Source=file:example.db?cipher=sqlcipher&legacy=4;Password=`. See [How to open an existing database encrypted with SQLCipher](https://github.com/utelle/SQLite3MultipleCiphers-NuGet#how-to-open-an-existing-database-encrypted-with-sqlcipher) for details. For more details, see [SQLite encryption options for use with SQLitePCLRaw](https://github.com/ericsink/SQLitePCL.raw/wiki/SQLite-encryption-options-for-use-with-SQLitePCLRaw) and [SQLitePCLRaw 3.0 Release Notes](https://github.com/ericsink/SQLitePCL.raw/blob/main/v3.md). @@ -378,7 +451,7 @@ Previously, the `SQLitePCLRaw.bundle_sqlite3`, `SQLitePCLRaw.bundle_winsqlite3`, ##### New behavior -Starting with SQLitePCLRaw 3.0 (used by Microsoft.Data.Sqlite 11.0), these bundle packages have been removed. If your application depended on one of these bundles, you must now reference the corresponding provider package and explicitly initialize it. +Starting with SQLitePCLRaw 3.0 (used by Microsoft.Data.Sqlite 11.0), these bundle packages have been removed. If your application depended on one of these bundles, use one of the following migration paths. ##### Why @@ -386,9 +459,7 @@ Each of these bundle packages contained only a single line of configuration code ##### Mitigations -Replace the removed bundle package with the corresponding provider package and add explicit initialization code. - -**If using `bundle_sqlite3` or `bundle_winsqlite3`**, replace the package reference: +**If using `bundle_sqlite3` or `bundle_winsqlite3`**, replace the removed bundle package with the corresponding provider package: ```xml @@ -418,6 +489,16 @@ static void Init() } ``` +**If using `bundle_e_sqlite3mc`**, replace the package reference with `SQLite3MC.PCLRaw.bundle`: + +```xml + + + + + +``` + **If using `bundle_green`**, the recommended migration path is to switch to `SQLitePCLRaw.bundle_e_sqlite3`. Alternatively, use `SQLitePCLRaw.config.e_sqlite3` paired with a separate native library package like `SourceGear.sqlite3`, which allows you to update the SQLite version independently: ```xml @@ -442,3 +523,80 @@ static void Init() > [!NOTE] > If you are using `SQLitePCLRaw.bundle_e_sqlite3`, no changes are required—just update the version number. See the [SQLitePCLRaw 3.0 Release Notes](https://github.com/ericsink/SQLitePCL.raw/blob/main/v3.md) for details. + +### Low-impact changes + + + +#### Microsoft.Data.Sqlite now bundles SQLite3 Multiple Ciphers + +[Tracking PR dotnet/efcore#38402](https://github.com/dotnet/efcore/pull/38402) + +##### Old behavior + +The `Microsoft.Data.Sqlite` package referenced `SQLitePCLRaw.bundle_e_sqlite3`, which provides the standard `e_sqlite3` native SQLite build. This build has no encryption support, so setting a password (for example, via `SqliteConnectionStringBuilder.Password` or the `Password` connection-string keyword) failed at runtime. + +##### New behavior + +Starting with `Microsoft.Data.Sqlite` 11.0, the package references `SQLite3MC.PCLRaw.bundle`, which provides the `e_sqlite3mc` native build ([SQLite3 Multiple Ciphers](https://github.com/utelle/SQLite3MultipleCiphers)). This build receives updates on NuGet.org more promptly than `SQLitePCLRaw.bundle_e_sqlite3`. + +As an added bonus, encryption (including setting a password) now works out of the box. See the [SQLite3 Multiple Ciphers documentation](https://github.com/utelle/SQLite3MultipleCiphers-NuGet#passphrase-based-database-encryption-support) for details on enabling passphrase-based database encryption. + +This change also applies to the EF Core SQLite provider (`Microsoft.EntityFrameworkCore.Sqlite`), which references `SQLite3MC.PCLRaw.bundle` through `Microsoft.Data.Sqlite`. + +##### Why + +The primary reason for the switch is maintenance and security: new versions of the `e_sqlite3` native build are no longer published to NuGet.org through `SQLitePCLRaw.bundle_e_sqlite3` in a timely manner, which means security fixes in upstream SQLite can be delayed. SQLite3 Multiple Ciphers is an actively maintained project that tracks upstream SQLite releases and ships updated builds promptly, so it was adopted as the default native build for `Microsoft.Data.Sqlite`. As an added bonus, it also supports encryption. This means it can replace the `SQLitePCLRaw.bundle_e_sqlcipher` package that was deprecated and removed (see [Encryption-enabled SQLite packages have been removed](#sqlite-encryption-removed)). + +##### Mitigations + +For most applications, **no action is required**. SQLite3 Multiple Ciphers is a superset of SQLite that behaves identically to the standard build for unencrypted databases—it only applies encryption when you explicitly supply a key or password. Existing unencrypted databases continue to open and work unchanged. + +Review the following cases, which may require action in some applications: + +- **Direct `SQLitePCLRaw.bundle_e_sqlite3` reference.** If your application directly references `SQLitePCLRaw.bundle_e_sqlite3`, it conflicts with the new `SQLite3MC.PCLRaw.bundle` dependency brought in by `Microsoft.Data.Sqlite` (or `Microsoft.EntityFrameworkCore.Sqlite`). Remove the direct `SQLitePCLRaw.bundle_e_sqlite3` reference unless you intentionally switch to the `.Core` packages shown below. + +- **Native library and provider name change.** The bundled native library is now `e_sqlite3mc` (rather than `e_sqlite3`), and the provider initialized by the bundle is `SQLite3Provider_e_sqlite3mc`. This matters if your application: + - References a specific native asset filename (for example, `e_sqlite3`) in publishing, trimming, AOT, or single-file configuration. Update those references to `e_sqlite3mc`. + +- **Platform (RID) coverage.** SQLite3 Multiple Ciphers doesn't currently include native binaries for every runtime identifier covered by `SourceGear.sqlite3`; for example, `linux-riscv64`, `linux-musl-riscv64`, and `linux-musl-s390x` aren't included. If you target a platform that the new bundle doesn't include, the native library may fail to load at runtime. In that case, revert to the standard build using the package references below. + +- **Linux glibc requirement and opt-out.** The bundled `e_sqlite3mc` library is prebuilt native code. On Linux, it currently requires glibc 2.33 or later; on older distributions, loading it can fail at runtime with an error such as `GLIBC_2.33 not found`. If the target system is unable to satisfy this requirement, follow the opt-out steps below. + +- **Reserved encryption keywords.** SQLite3 Multiple Ciphers reserves certain connection-string/URI parameters and PRAGMAs (such as `key`, `hexkey`, and `cipher`) for encryption configuration. This is unlikely to affect typical applications, but if you happened to use these names for unrelated purposes, behavior may differ. + +- **Double-quoted string literal support.** `e_sqlite3mc` doesn't include SQLite's legacy support for double-quoted string literals. If your SQL uses double quotes for string values, change it to use single quotes; double quotes should be used only for identifiers. Review raw SQL in your application (for example, SQL passed to `FromSql`, `ExecuteSql`, or migrations operations), and use SQL logging or integration tests to identify affected commands. + +If you want to keep using the standard, non-encrypted `e_sqlite3` build, reference `Microsoft.Data.Sqlite.Core` together with `SQLitePCLRaw.bundle_e_sqlite3` instead of the `Microsoft.Data.Sqlite` meta-package: + +```xml + + +``` + +For EF Core, reference `Microsoft.EntityFrameworkCore.Sqlite.Core` instead of `Microsoft.EntityFrameworkCore.Sqlite` and add the standard bundle: + +```xml + + +``` + +If you need to use a system-installed SQLite library instead of a bundled one, reference `Microsoft.Data.Sqlite.Core` together with `SQLitePCLRaw.provider.sqlite3` instead of the `Microsoft.Data.Sqlite` meta-package: + +```xml + + +``` + +For EF Core: + +```xml + + +``` + +And initialize the provider explicitly before using SQLite: + +```csharp +SQLitePCL.raw.SetProvider(new SQLitePCL.SQLite3Provider_sqlite3()); +``` 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 5f48f20243..65e03a56f4 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: SamMonoRT -ms.date: 04/08/2026 +ms.date: 06/22/2026 uid: core/what-is-new/ef-core-11.0/provider-facing-changes --- @@ -13,10 +13,13 @@ 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. +* Type mapping has been made generic to support NativeAOT ([PR #38440](https://github.com/dotnet/efcore/pull/38440)). Provider maintainers should: (1) update custom mappings to derive from the new generic mapping base types (`CoreTypeMapping` and `RelationalTypeMapping` where applicable) so default comparers can be created without reflection, and (2) update calls/overrides of `CoreTypeMapping.Clone(...)` and `RelationalTypeMapping.Clone(...)` to remove the old `clrType` argument. * 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. * EF Core now strips no-op SQL CASTs - i.e. `SqlUnaryExpression(Convert)` nodes whose store type matches that of their operand ([see #36247](https://github.com/dotnet/efcore/issues/36247), [PR #38156](https://github.com/dotnet/efcore/pull/38156)). This can flush out imprecise translations which assigned an imprecise store type to a node and then wrapped it with a Convert; because the store types now match, the Convert is stripped, and the imprecise store type may propagate. Review your translations to ensure that the operand of a Convert node has the correct, precise store type. +* Keys and indexes can now traverse complex-type properties ([PR #38192](https://github.com/dotnet/efcore/pull/38192)). The metadata API for indexes and keys has been broadened (e.g., `FindIndex`/`AddIndex` signatures now accept `IReadOnlyProperty`, new `ModelValidator` checks, and `ICSharpHelper` additions). Providers that consume index/key metadata, generate C# for indexes (scaffolding/codegen), or implement custom validation should review and update their code to handle complex-type-spanning keys and indexes. ## 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`. +* EF Core's Specification.Tests assemblies have been upgraded from xUnit v2 to xUnit v3 ([PR #38277](https://github.com/dotnet/efcore/pull/38277)). Provider test projects that reference these assemblies and inherit from the base test classes must be migrated to xUnit v3: update to the new `xunit.v3` packages, `Microsoft.DotNet.XUnitV3Extensions`, `Microsoft.Testing.Platform` runner, and `Microsoft.NET.Test.Sdk`; account for `[Collection]`/`ITestOutputHelper` API differences; and change `[ConditionalFact]` to `[Fact]` and `[ConditionalTheory]` to `[Theory]`. When running tests directly with `dotnet exec`, add `--filter-not-trait category=failing --ignore-exit-code 8`. 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 64f8cd36a5..8aef322ac0 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 @@ -2,7 +2,7 @@ title: What's New in EF Core 11 description: Overview of new features in EF Core 11 author: SamMonoRT -ms.date: 04/22/2026 +ms.date: 06/10/2026 uid: core/what-is-new/ef-core-11.0/whatsnew --- @@ -106,6 +106,39 @@ modelBuilder.Entity() This simplifies model configuration by removing the need to explicitly navigate through intermediate complex type builders to reach the property you want to configure. + + +### Keys and indexes on complex type properties + +Keys and indexes can now use scalar properties nested inside non-collection complex types: + +```csharp +modelBuilder.Entity() + .HasKey(c => c.CustomerId.Value); + +modelBuilder.Entity() + .HasIndex(c => c.Address.PostalCode); +``` + +The same paths can be configured by name: + +```csharp +modelBuilder.Entity() + .HasIndex("Address.PostalCode"); +``` + +For relational providers, indexes can also target paths inside complex types mapped to JSON columns. Complex collection paths use `[]` for all elements or a numeric indexer for a specific element: + +```csharp +modelBuilder.Entity() + .ComplexCollection(o => o.Items, b => b.ToJson()); + +modelBuilder.Entity() + .HasIndex("Items[].Sku"); +``` + +For more information, see [Keys](xref:core/modeling/keys#keys-on-complex-type-properties) and [Indexes](xref:core/modeling/indexes#indexes-on-complex-type-properties). + ### Stabilization and bug fixes @@ -187,6 +220,30 @@ Both optimizations can have a significant positive impact on query performance, More details on the benchmark are available [here](https://github.com/dotnet/efcore/issues/29182#issuecomment-4231140289), and as always, actual performance in your application will vary based on your schema, data and a variety of other factors. + + +### Support for the new .NET 11 `FullJoin` operator + +.NET 11 adds first-class LINQ support for `FullJoin`, which keeps rows from both input collections and matches them when keys are equal. EF Core 11 translates this operator to `FULL JOIN` on relational databases: + +```csharp +var results = await context.Customers + .FullJoin( + context.Orders, + c => c.Id, + o => o.CustomerId, + (c, o) => new { Customer = c, Order = o }) + .ToListAsync(); +``` + +This generates SQL similar to the following: + +```sql +SELECT [c].[Id], [c].[Name], [o].[Id], [o].[CustomerId], [o].[OrderDate] +FROM [Customers] AS [c] +FULL JOIN [Orders] AS [o] ON [c].[Id] = [o].[CustomerId] +``` + ### Stripping of no-op CASTs @@ -374,6 +431,47 @@ Both methods return `FullTextSearchResult`, giving you access to both t For more information, see the [full documentation on full-text search](xref:core/providers/sql-server/full-text-search). + + +#### Hybrid search + +SQL Server vector search can be combined with full-text table-valued functions to implement _hybrid search_: full-text search finds exact linguistic matches, vector search finds semantically similar results, and the two rankings are merged. EF Core 11's `FullJoin` support makes this pattern more complete, since highly-ranked rows from either side can contribute to the final results: + +```csharp +var results = await context.Articles + .FreeTextTable(textualQuery, topN: k) + .Join( + context.Articles, + fts => fts.Key, + a => a.Id, + (fts, a) => new { Article = a, fts.Rank }) + .FullJoin( + context.Articles.VectorSearch(a => a.Embedding, queryEmbedding, "cosine") + .OrderBy(r => r.Distance) + .Take(k) + .WithApproximate(), + fts => fts.Article.Id, + vs => vs.Value.Id, + (fts, vs) => new + { + Article = fts != null ? fts.Article : vs.Value, + FullTextRank = fts == null ? null : (int?)fts.Rank, + VectorDistance = vs == null ? null : (double?)vs.Distance + }) + .Select(x => new + { + x.Article, + RrfScore = (x.FullTextRank == null ? 0.0 : 1.0 / (k + x.FullTextRank.Value)) + + (x.VectorDistance == null ? 0.0 : 1.0 / (k + x.VectorDistance.Value)) + }) + .OrderByDescending(x => x.RrfScore) + .Take(10) + .Select(x => x.Article) + .ToListAsync(); +``` + +For more information, see the [SQL Server vector search documentation](xref:core/providers/sql-server/vector-search#hybrid-search). + ### Contains operations using JSON_CONTAINS @@ -444,6 +542,29 @@ WHERE JSON_CONTAINS([b].[JsonData], 8, N'$.Rating') = 1 For the full `JSON_CONTAINS` SQL Server documentation, see [`JSON_CONTAINS`](/sql/t-sql/functions/json-contains-transact-sql). + + +### JSON indexes + +EF Core 11 can create and scaffold [SQL Server JSON indexes](/sql/t-sql/statements/create-json-index-transact-sql) for paths inside complex types mapped to JSON columns: + +```csharp +modelBuilder.Entity() + .ComplexProperty(c => c.Contact, b => b.ToJson().HasColumnType("json")); + +modelBuilder.Entity() + .HasIndex("Contact.Address.City"); +``` + +This generates SQL similar to: + +```sql +CREATE JSON INDEX [IX_Customers_Contact_Address_City] +ON [Customers]([Contact]) FOR (N'$.Address.City'); +``` + +For more information, see [JSON indexes](xref:core/providers/sql-server/index#json-indexes). + ### Temporal period properties mapped to CLR properties @@ -504,6 +625,48 @@ For the complete list of date/time function translations, see the [SQL Server fu * now defaults to compatibility level 160 (SQL Server 2022), enabling SQL Server 2022-specific translations such as `LEAST` and `GREATEST` by default; see the [breaking change note](xref:core/what-is-new/ef-core-11.0/breaking-changes#sqlserver-compatibility-level-160) for more information. +## SQLite + + + +### Ordering in string aggregation + +EF Core can translate `string.Join` and `string.Concat` over a grouping to SQLite's `group_concat` aggregate function. Starting with EF 11, these translations also support ordering the aggregated values, by applying `OrderBy`/`OrderByDescending` inside the aggregate. Previously, queries that ordered the values fell back to client evaluation. + +For example, the following query concatenates each blog's post titles, ordered by descending post `Id`: + +```csharp +var blogs = await context.Blogs + .Select(b => new + { + b.Name, + Posts = string.Join( + ", ", + b.Posts.OrderByDescending(p => p.Id).Select(p => p.Title)) + }) + .ToListAsync(); +``` + +This translates to the following SQL, which uses `group_concat` with an `ORDER BY` clause: + +```sql +SELECT "b"."Name", COALESCE(group_concat("p"."Title", ', ' ORDER BY "p"."Id" DESC), '') AS "Posts" +FROM "Blogs" AS "b" +LEFT JOIN "Posts" AS "p" ON "b"."Id" = "p"."BlogId" +GROUP BY "b"."Id", "b"."Name" +``` + +Ordering inside `group_concat` requires SQLite 3.44.0 or later. + + + +### UInt128 support + +`Microsoft.Data.Sqlite` can now bind parameter values. The value is stored as a zero-padded, 39-digit text representation, which preserves correct ordering and comparison of values directly in the database. + +> [!NOTE] +> Reading values from data readers is not yet supported. + ## Cosmos DB @@ -538,6 +701,31 @@ Complex types are generally a better fit than owned types when mapping to JSON d This feature was contributed by [@JoasE](https://github.com/JoasE) - many thanks! + + +### Indexes and indexing policy + +EF Core 11 adds support for more Azure Cosmos DB indexing policy configuration. You can now disable automatic indexing, exclude individual paths when automatic indexing is enabled, and emit explicit single-property, composite, vector, and full-text indexes: + +```csharp +modelBuilder.Entity(b => +{ + b.HasAutomaticIndexing() + .Except("/InternalNotes/?"); + + b.HasIndex(o => new { o.CustomerId, o.OrderDate }); +}); +``` + +Indexes can also traverse complex type properties and complex collections: + +```csharp +modelBuilder.Entity() + .HasIndex(o => o.Items.Select(i => i.Sku)); +``` + +For more information, see [Indexing policy](xref:core/providers/cosmos/modeling#indexing-policy). + ### Transactional batches @@ -711,6 +899,23 @@ Explicit command-line options always take precedence over configuration file val For more information, see [Configuration file](xref:core/cli/dotnet#configuration-file). + + +### Wildcard context support for migration commands + +When a project defines multiple `DbContext` types, several `dotnet ef` commands accept a wildcard (`*`) as the value of the `--context` option to target all contexts at once, instead of running the command separately for each one. The commands that support the wildcard are: + +* `dotnet ef migrations list` — lists the migrations for every context. +* `dotnet ef migrations script` — generates and concatenates a script for every context. +* `dotnet ef database update` — applies migrations to the database of every context (migration bundles are covered as well). +* `dotnet ef database drop` — drops the database for every context. + +For example, the following command lists the migrations for all contexts in the project: + +```dotnetcli +dotnet ef migrations list --context "*" +``` + ## 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.