dotnet/efcore · error · NotSupportedException

The Cosmos database provider does not support transactions.

Error message

The Cosmos database provider does not support transactions.

What it means

Cosmos DB does not support distributed/ACID transactions in the way relational providers do, so the Cosmos EF Core provider's IDbContextTransactionManager.BeginTransaction() unconditionally throws NotSupportedException (CosmosStrings.TransactionsNotSupported). Cosmos only guarantees atomicity within a single stored procedure; multi-document transactions are not exposed through this provider.

Source

Thrown at src/EFCore.Cosmos/Storage/Internal/CosmosTransactionManager.cs:24

namespace Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal;

/// <summary>
///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
///     the same compatibility standards as public APIs. It may be changed or removed without notice in
///     any release. You should only use it directly in your code with extreme caution and knowing that
///     doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public class CosmosTransactionManager : IDbContextTransactionManager, ITransactionEnlistmentManager
{
    /// <summary>
    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
    ///     any release. You should only use it directly in your code with extreme caution and knowing that
    ///     doing so can result in application failures when updating to a new Entity Framework Core release.
    /// </summary>
    public virtual IDbContextTransaction BeginTransaction()
        => throw new NotSupportedException(CosmosStrings.TransactionsNotSupported);

    /// <summary>
    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
    ///     any release. You should only use it directly in your code with extreme caution and knowing that
    ///     doing so can result in application failures when updating to a new Entity Framework Core release.
    /// </summary>
    public virtual Task<IDbContextTransaction> BeginTransactionAsync(
        CancellationToken cancellationToken = default)
        => throw new NotSupportedException(CosmosStrings.TransactionsNotSupported);

    /// <summary>
    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
    ///     any release. You should only use it directly in your code with extreme caution and knowing that
    ///     doing so can result in application failures when updating to a new Entity Framework Core release.
    /// </summary>
    public virtual void CommitTransaction()

View on GitHub (pinned to dbf9771522)

Solutions

  1. Remove the explicit BeginTransaction/Commit/Rollback wrapper when using the Cosmos provider; rely on SaveChanges atomicity per document.
  2. Guard the transaction call with a provider check: if (db.Database.ProviderName != "Microsoft.EntityFrameworkCore.Cosmos") { ... BeginTransaction ... }.
  3. For multi-document atomicity, encapsulate the operation in a Cosmos stored procedure instead of an EF transaction.
  4. Refactor shared UoW code to make transaction usage opt-in per provider.

Example fix

// before
using var tx = db.Database.BeginTransaction();
try { await db.SaveChangesAsync(); tx.Commit(); }
catch { tx.Rollback(); throw; } // NotSupportedException on Cosmos

// after
await db.SaveChangesAsync(); // Cosmos: each document save is atomic; no tx wrapper
// or guard:
if (db.Database.ProviderName != "Microsoft.EntityFrameworkCore.Cosmos")
{
    using var tx = db.Database.BeginTransaction();
    await db.SaveChangesAsync();
    tx.Commit();
}
else
{
    await db.SaveChangesAsync();
}
Defensive patterns

Strategy: validation

Validate before calling

// Skip transaction wrapping on Cosmos
var useTx = db.Database.ProviderName != "Microsoft.EntityFrameworkCore.Cosmos";
if (useTx)
{
    using var tx = db.Database.BeginTransaction();
    try { db.SaveChanges(); tx.Commit(); }
    catch { tx.Rollback(); throw; }
}
else
{
    db.SaveChanges();
}

Type guard

static bool SupportsTransactions(DbContext db)
    => db.Database.ProviderName != "Microsoft.EntityFrameworkCore.Cosmos";

Try / catch

try
{
    if (db.Database.ProviderName != "Microsoft.EntityFrameworkCore.Cosmos")
        using (db.Database.BeginTransaction()) { db.SaveChanges(); }
    else
        db.SaveChanges();
}
catch (NotSupportedException ex) when (ex.Message.Contains("does not support transactions"))
{
    // Fallback: save without transaction (Cosmos saves are per-document atomic)
    db.SaveChanges();
}

Prevention

When it happens

Trigger: Calling dbContext.Database.BeginTransaction() (or any API that forces a transaction such as ExecuteSqlRaw inside a TransactionScope) when the Cosmos provider is registered. Any generic data-access layer that opens a transaction around SaveChanges regardless of provider will hit this on the sync path.

Common situations: Shared/generic repository code that wraps SaveChanges in using var tx = db.Database.BeginTransaction(); migrating from SQL Server/SQLite to Cosmos without removing the transaction wrapper; code that mixes TransactionScope with Cosmos; third-party libraries (MediatR behaviors, UoW patterns) that unconditionally open transactions.

Related errors


AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06). Data as JSON: /api/errors/0d1a5ad69de085cf. Report an issue: GitHub.