From c371c3ea2b4f1103555ad1d57a1218072139599a Mon Sep 17 00:00:00 2001 From: JoasE <32096708+JoasE@users.noreply.github.com> Date: Thu, 11 Sep 2025 15:50:42 +0200 Subject: [PATCH 1/4] Add documentation for cosmos db provider transactional batches --- .../core/providers/cosmos/limitations.md | 1 + .../providers/cosmos/savechanges-atomicity.md | 36 +++++++++++++++++++ entity-framework/toc.yml | 2 ++ 3 files changed, 39 insertions(+) create mode 100644 entity-framework/core/providers/cosmos/savechanges-atomicity.md diff --git a/entity-framework/core/providers/cosmos/limitations.md b/entity-framework/core/providers/cosmos/limitations.md index 26cc0f153c..a9a6580c42 100644 --- a/entity-framework/core/providers/cosmos/limitations.md +++ b/entity-framework/core/providers/cosmos/limitations.md @@ -11,6 +11,7 @@ The Azure Cosmos DB database provider targets the Azure Cosmos DB NoSQL store, w Common EF Core patterns that either do not apply, or are a pit-of-failure, when using a document database include: +- [Transactions](../../saving/transactions) are not supported, since Azure Cosmos DB does not implement relational-style transactions, as is typical of most document databases. However, the EF Core Azure Cosmos DB provider will use [Transactional batches](azure/cosmos-db/nosql/transactional-batch) when possible. See [Atomicity of SaveChanges](savechanges-atomicity) for further details. - Schema migration is not supported, since there is no defined schema for the documents. However, there could be other mechanisms for dealing with evolving data shapes that do make sense with Azure Cosmos DB NoSQL, For example, [Schema versioning pattern with Azure Cosmos DB](https://github.com/dotnet/efcore/issues/23753), and [Azure Cosmos DB data migration](https://github.com/dotnet/efcore/issues/11099). - Reverse-engineering (scaffolding) a model from an existing database is not supported. Again, this is not supported because there is no defined database schema to scaffold from. However, see [Use shape of documents in the Azure Cosmos DB database to scaffold a schema](https://github.com/dotnet/efcore/issues/30290). - Schema concepts defined on the EF model, like indexes and constraints, are ignored when using a document database, since there is no schema. Note that Azure Cosmos DB NoSQL performs [automatic indexing of documents](/azure/cosmos-db/index-overview). diff --git a/entity-framework/core/providers/cosmos/savechanges-atomicity.md b/entity-framework/core/providers/cosmos/savechanges-atomicity.md new file mode 100644 index 0000000000..0e94f9fd88 --- /dev/null +++ b/entity-framework/core/providers/cosmos/savechanges-atomicity.md @@ -0,0 +1,36 @@ +--- +title: Azure Cosmos DB Provider - Atomicity of SaveChanges - EF Core +description: Explains the atomicity of SaveChanges within the Entity Framework Core Azure Cosmos DB provider as compared to other providers +author: JoasE +ms.date: 09/10/2025 +uid: core/providers/cosmos/savechanges-atomicity +--- +# EF Core Azure Cosmos DB Provider Atomicity of SaveChanges + +Azure Cosmos DB does not support transactions in the relational database sense. That is, there is no concept of a single atomic transaction spanning arbitrary operations across containers or partitions. This is a common limitation of document databases, where the focus is on scalability and availability rather than strict transactional semantics. + +Instead of transactions, Azure Cosmos DB provides support for [transactional batches](/azure/cosmos-db/nosql/transactional-batch). A transactional batch allows up to 100 operations to be executed together as a batch within a single partition. Atomicity is guaranteed within a single batch: if any operation fails, the entire batch is rolled back and none of its changes are applied. However, once a batch is written, it cannot be rolled back or deferred, and atomicity cannot be enforced across multiple batches. + +The EF Core Azure Cosmos DB provider leverages transactional batches whenever possible, providing a best-effort approximation of atomicity when saving changes. + +## How EF Core saves changes in batches + +When SaveChanges or SaveChangesAsync is called, the provider groups pending changes into transactional batches. Each batch contains up to 100 entries, grouped by container and partition key. These batches are then executed sequentially. If a batch fails, execution stops immediately and no subsequent batches are attempted. Any batches that were successfully committed before the failure remain saved. + +> [!WARNING] +> Azure Cosmos DB does not allow document writes with [pre- or post-triggers](/azure/cosmos-db/nosql/stored-procedures-triggers-udfs#triggers) to be part of a transactional batch. Because of this, any entities configured with triggers are executed separately and before any transactional batches. This can affect ordering and consistency in mixed scenarios. + +## Controlling batching behavior + +The batching behavior of the EF Core Azure Cosmos DB provider is controlled by the property. This setting allows developers to trade off between performance, consistency guarantees, and failure behavior depending on the application’s needs. + +* **Auto** (default) – Entries are grouped into transactional batches as described above. This generally provides good performance for writing to multiple entities within the same partition with a best-effort approximation of atomicity. +* **Never** – All entries are written individually, in the exact order they were tracked. This avoids batching and can be slower, especially for large numbers of entries. +* **Always** – Requires that all changes can be executed as a single atomic operation. If any entries cannot be included in a batch (for example, due to partitioning, exceeding 100 items, or there are multiple changed entries including one with triggers), the provider will throw an exception. + +```csharp +using var context = new BlogsContext(); +context.Database.AutoTransactionBehavior = AutoTransactionBehavior.Always; +context.AddRange(Enumerable.Range(0, 101).Select(i => new Post())); +await context.SaveChangesAsync(); // Throws InvalidOperationException +``` diff --git a/entity-framework/toc.yml b/entity-framework/toc.yml index 981f1181ee..0604d765cf 100644 --- a/entity-framework/toc.yml +++ b/entity-framework/toc.yml @@ -442,6 +442,8 @@ href: core/providers/cosmos/vector-search.md - name: Full text search href: core/providers/cosmos/full-text-search.md + - name: Atomicity of SaveChanges + href: core/providers/cosmos/savechanges-atomicity.md - name: Azure Cosmos DB limitations href: core/providers/cosmos/limitations.md - name: End-to-end sample From 541d37b50db801a669c6f0c1b9ae2d9a73b835db Mon Sep 17 00:00:00 2001 From: JoasE <32096708+JoasE@users.noreply.github.com> Date: Mon, 29 Sep 2025 15:09:22 +0200 Subject: [PATCH 2/4] Update entity-framework/core/providers/cosmos/limitations.md Co-authored-by: Shay Rojansky --- entity-framework/core/providers/cosmos/limitations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/entity-framework/core/providers/cosmos/limitations.md b/entity-framework/core/providers/cosmos/limitations.md index a9a6580c42..abb415b4f9 100644 --- a/entity-framework/core/providers/cosmos/limitations.md +++ b/entity-framework/core/providers/cosmos/limitations.md @@ -11,7 +11,7 @@ The Azure Cosmos DB database provider targets the Azure Cosmos DB NoSQL store, w Common EF Core patterns that either do not apply, or are a pit-of-failure, when using a document database include: -- [Transactions](../../saving/transactions) are not supported, since Azure Cosmos DB does not implement relational-style transactions, as is typical of most document databases. However, the EF Core Azure Cosmos DB provider will use [Transactional batches](azure/cosmos-db/nosql/transactional-batch) when possible. See [Atomicity of SaveChanges](savechanges-atomicity) for further details. +Like most document databases, Azure Cosmos DB does not implement relational-style transactions, which provide full ACID guarantees across any number of operations and tables. However, the EF Core Azure Cosmos DB provider uses [Transactional batches](azure/cosmos-db/nosql/transactional-batch) when possible; see [Atomicity of SaveChanges](savechanges-atomicity) for further details. - Schema migration is not supported, since there is no defined schema for the documents. However, there could be other mechanisms for dealing with evolving data shapes that do make sense with Azure Cosmos DB NoSQL, For example, [Schema versioning pattern with Azure Cosmos DB](https://github.com/dotnet/efcore/issues/23753), and [Azure Cosmos DB data migration](https://github.com/dotnet/efcore/issues/11099). - Reverse-engineering (scaffolding) a model from an existing database is not supported. Again, this is not supported because there is no defined database schema to scaffold from. However, see [Use shape of documents in the Azure Cosmos DB database to scaffold a schema](https://github.com/dotnet/efcore/issues/30290). - Schema concepts defined on the EF model, like indexes and constraints, are ignored when using a document database, since there is no schema. Note that Azure Cosmos DB NoSQL performs [automatic indexing of documents](/azure/cosmos-db/index-overview). From 1470823b86bc8361258047363f99c90b1d4d0b4d Mon Sep 17 00:00:00 2001 From: JoasE <32096708+JoasE@users.noreply.github.com> Date: Mon, 29 Sep 2025 15:13:53 +0200 Subject: [PATCH 3/4] rename page to saving --- entity-framework/core/providers/cosmos/limitations.md | 2 +- .../cosmos/{savechanges-atomicity.md => saving.md} | 8 ++++---- entity-framework/toc.yml | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) rename entity-framework/core/providers/cosmos/{savechanges-atomicity.md => saving.md} (91%) diff --git a/entity-framework/core/providers/cosmos/limitations.md b/entity-framework/core/providers/cosmos/limitations.md index a9a6580c42..bc35952211 100644 --- a/entity-framework/core/providers/cosmos/limitations.md +++ b/entity-framework/core/providers/cosmos/limitations.md @@ -11,7 +11,7 @@ The Azure Cosmos DB database provider targets the Azure Cosmos DB NoSQL store, w Common EF Core patterns that either do not apply, or are a pit-of-failure, when using a document database include: -- [Transactions](../../saving/transactions) are not supported, since Azure Cosmos DB does not implement relational-style transactions, as is typical of most document databases. However, the EF Core Azure Cosmos DB provider will use [Transactional batches](azure/cosmos-db/nosql/transactional-batch) when possible. See [Atomicity of SaveChanges](savechanges-atomicity) for further details. +- [Transactions](../../saving/transactions) are not supported, since Azure Cosmos DB does not implement relational-style transactions, as is typical of most document databases. However, the EF Core Azure Cosmos DB provider will use [Transactional batches](azure/cosmos-db/nosql/transactional-batch) when possible. See [Saving data](saving) for further details. - Schema migration is not supported, since there is no defined schema for the documents. However, there could be other mechanisms for dealing with evolving data shapes that do make sense with Azure Cosmos DB NoSQL, For example, [Schema versioning pattern with Azure Cosmos DB](https://github.com/dotnet/efcore/issues/23753), and [Azure Cosmos DB data migration](https://github.com/dotnet/efcore/issues/11099). - Reverse-engineering (scaffolding) a model from an existing database is not supported. Again, this is not supported because there is no defined database schema to scaffold from. However, see [Use shape of documents in the Azure Cosmos DB database to scaffold a schema](https://github.com/dotnet/efcore/issues/30290). - Schema concepts defined on the EF model, like indexes and constraints, are ignored when using a document database, since there is no schema. Note that Azure Cosmos DB NoSQL performs [automatic indexing of documents](/azure/cosmos-db/index-overview). diff --git a/entity-framework/core/providers/cosmos/savechanges-atomicity.md b/entity-framework/core/providers/cosmos/saving.md similarity index 91% rename from entity-framework/core/providers/cosmos/savechanges-atomicity.md rename to entity-framework/core/providers/cosmos/saving.md index 0e94f9fd88..8a17c1086b 100644 --- a/entity-framework/core/providers/cosmos/savechanges-atomicity.md +++ b/entity-framework/core/providers/cosmos/saving.md @@ -1,11 +1,11 @@ --- -title: Azure Cosmos DB Provider - Atomicity of SaveChanges - EF Core -description: Explains the atomicity of SaveChanges within the Entity Framework Core Azure Cosmos DB provider as compared to other providers +title: Saving data - Azure Cosmos DB Provider - EF Core +description: Explains saving data with the Azure Cosmos DB Provider as compared to other providers author: JoasE ms.date: 09/10/2025 -uid: core/providers/cosmos/savechanges-atomicity +uid: core/providers/cosmos/saving --- -# EF Core Azure Cosmos DB Provider Atomicity of SaveChanges +# Saving data Azure Cosmos DB does not support transactions in the relational database sense. That is, there is no concept of a single atomic transaction spanning arbitrary operations across containers or partitions. This is a common limitation of document databases, where the focus is on scalability and availability rather than strict transactional semantics. diff --git a/entity-framework/toc.yml b/entity-framework/toc.yml index 0604d765cf..97af18deb8 100644 --- a/entity-framework/toc.yml +++ b/entity-framework/toc.yml @@ -442,8 +442,8 @@ href: core/providers/cosmos/vector-search.md - name: Full text search href: core/providers/cosmos/full-text-search.md - - name: Atomicity of SaveChanges - href: core/providers/cosmos/savechanges-atomicity.md + - name: Save data + href: core/providers/cosmos/saving.md - name: Azure Cosmos DB limitations href: core/providers/cosmos/limitations.md - name: End-to-end sample From 39bdfd2af5b4a78dd0dbf65bf6d21259ba3d689d Mon Sep 17 00:00:00 2001 From: JoasE <32096708+JoasE@users.noreply.github.com> Date: Mon, 29 Sep 2025 16:11:00 +0200 Subject: [PATCH 4/4] reword and merge sections for saving cosmos db --- .../core/providers/cosmos/saving.md | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/entity-framework/core/providers/cosmos/saving.md b/entity-framework/core/providers/cosmos/saving.md index 8a17c1086b..43d42b07ae 100644 --- a/entity-framework/core/providers/cosmos/saving.md +++ b/entity-framework/core/providers/cosmos/saving.md @@ -7,30 +7,20 @@ uid: core/providers/cosmos/saving --- # Saving data -Azure Cosmos DB does not support transactions in the relational database sense. That is, there is no concept of a single atomic transaction spanning arbitrary operations across containers or partitions. This is a common limitation of document databases, where the focus is on scalability and availability rather than strict transactional semantics. - -Instead of transactions, Azure Cosmos DB provides support for [transactional batches](/azure/cosmos-db/nosql/transactional-batch). A transactional batch allows up to 100 operations to be executed together as a batch within a single partition. Atomicity is guaranteed within a single batch: if any operation fails, the entire batch is rolled back and none of its changes are applied. However, once a batch is written, it cannot be rolled back or deferred, and atomicity cannot be enforced across multiple batches. - -The EF Core Azure Cosmos DB provider leverages transactional batches whenever possible, providing a best-effort approximation of atomicity when saving changes. - -## How EF Core saves changes in batches - -When SaveChanges or SaveChangesAsync is called, the provider groups pending changes into transactional batches. Each batch contains up to 100 entries, grouped by container and partition key. These batches are then executed sequentially. If a batch fails, execution stops immediately and no subsequent batches are attempted. Any batches that were successfully committed before the failure remain saved. +In Azure Cosmos DB there is limited support for atomic transactions. This is a common limitation of document databases, where the focus is on scalability and availability rather than strict transactional semantics. Azure Cosmos DB supports [transactional batch](/azure/cosmos-db/nosql/transactional-batch) which allows up to 100 operations to be executed together as a batch within a single partition. Atomicity is guaranteed within a single batch: if any operation fails, the entire batch is rolled back and none of its changes are applied. However, once a batch is written, it cannot be rolled back or deferred, and atomicity cannot be enforced across multiple batches. > [!WARNING] > Azure Cosmos DB does not allow document writes with [pre- or post-triggers](/azure/cosmos-db/nosql/stored-procedures-triggers-udfs#triggers) to be part of a transactional batch. Because of this, any entities configured with triggers are executed separately and before any transactional batches. This can affect ordering and consistency in mixed scenarios. -## Controlling batching behavior - -The batching behavior of the EF Core Azure Cosmos DB provider is controlled by the property. This setting allows developers to trade off between performance, consistency guarantees, and failure behavior depending on the application’s needs. +By default, the EF Core Azure Cosmos DB provider leverages transactional batches whenever possible, providing a best-effort approximation of atomicity when saving changes. The batching behavior is controlled by the property. This setting allows developers to trade off between performance, consistency guarantees, and failure behavior depending on the application’s needs. -* **Auto** (default) – Entries are grouped into transactional batches as described above. This generally provides good performance for writing to multiple entities within the same partition with a best-effort approximation of atomicity. -* **Never** – All entries are written individually, in the exact order they were tracked. This avoids batching and can be slower, especially for large numbers of entries. +* **Auto** (default) – Entries are grouped into transactional batches. Each batch contains up to 100 entries, grouped by container and partition key. These batches are then executed sequentially. If a batch fails, execution stops immediately and no subsequent batches are attempted. Any batches that were successfully committed before the failure remain saved.This generally provides good performance for writing to multiple entities within the same partition with a best-effort approximation of atomicity. +* **Never** – All entries are written individually and sequentially, in the exact order they were tracked. This avoids batching and can be slower, especially for large numbers of entries. * **Always** – Requires that all changes can be executed as a single atomic operation. If any entries cannot be included in a batch (for example, due to partitioning, exceeding 100 items, or there are multiple changed entries including one with triggers), the provider will throw an exception. ```csharp using var context = new BlogsContext(); context.Database.AutoTransactionBehavior = AutoTransactionBehavior.Always; -context.AddRange(Enumerable.Range(0, 101).Select(i => new Post())); +context.AddRange(Enumerable.Range(0, 101).Select(i => new Post())); // 101 entries exceeds the batch item limit of 100. await context.SaveChangesAsync(); // Throws InvalidOperationException ```