EF Core 11 liga transactional batches de Cosmos DB por padrão
EF Core 11 agrupa writes de Cosmos DB em transactional batches por container e partition em cada SaveChanges, dando atomicidade best-effort e menos roundtrips sem mudanças de código.
EF Core 11 mudou silenciosamente como o provider do Azure Cosmos DB salva dados. Até o EF Core 10 todo insert, update, ou delete trackeado ia pro Cosmos como request próprio, o que significava que um SaveChangesAsync de N rows virava N chamadas HTTP separadas, N conjuntos de cobranças RU, e nenhuma atomicidade. Começando com EF Core 11 o provider agrupa essas operações em transactional batches do Cosmos DB automaticamente. Você não precisa fazer opt-in e não precisa reescrever seu código de data access.
O que mudou no SaveChanges
Um transactional batch no Cosmos empacota até 100 point operations que miram o mesmo container e a mesma logical partition num único roundtrip, executado atomicamente no lado server. EF Core 11 agora inspeciona o change tracker, agrupa entries por container e partition key, e emite um batch por grupo. As release notes do EF Core 11 descrevem o comportamento: batches são executados sequencialmente, e se um batch falha, os subsequentes não são executados.
O comportamento é controlado pela nova opção AutoTransactionBehavior:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseCosmos(
connectionString: Configuration["Cosmos:ConnectionString"],
databaseName: "OrdersDB",
cosmosOptions =>
{
// Auto is the new default in EF Core 11.
// Never reproduces the pre-11 one-request-per-entry behavior.
// Always forces the whole SaveChanges to fit in one batch.
});
}
Auto agrupa o que puder. Never restaura o comportamento antigo de request por entry se precisar por compatibilidade. Always é útil quando seu domínio exige um write all-or-nothing e você quer que o EF jogue no save em vez de te deixar com uma mutação half-applied.
Por que o agrupamento por partition importa
Porque batches são escopados a uma logical partition, a forma dos seus writes agora afeta diretamente quantos roundtrips você paga. Escrever dez orders que compartilham a mesma CustomerId partition key é um único batch. Escrever dez orders pra dez customers diferentes é dez batches. Considere esse modelo:
public class Order
{
public Guid Id { get; set; }
public string CustomerId { get; set; } = null!;
public decimal Total { get; set; }
public List<OrderItem> Items { get; set; } = new();
}
public class OrdersContext : DbContext
{
public DbSet<Order> Orders => Set<Order>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>()
.ToContainer("Orders")
.HasPartitionKey(o => o.CustomerId);
}
}
Um job noturno que insere vinte novas orders e atualiza seus totais pra um customer agora bate no Cosmos uma vez, não quarenta:
await using var context = new OrdersContext();
for (int i = 0; i < 20; i++)
{
context.Orders.Add(new Order
{
Id = Guid.NewGuid(),
CustomerId = "cust-42",
Total = 0m
});
}
// Single transactional batch, atomic, one roundtrip.
await context.SaveChangesAsync();
Se você precisa de atomicidade estrita pode setar AutoTransactionBehavior.Always por context. EF vai jogar se o working set precisasse de mais de um batch (partitions diferentes, containers diferentes, ou mais do que o limite de serviço de operações), o que força o problema a aflorar nos seus testes em vez de em produção depois de um write parcial.
Quando desligar
Ainda há casos onde Never é a resposta certa. Se seu code path depende de uma falha específica isolada a um único document (por exemplo, um upsert best-effort em que você quer continuar em conflict), as semânticas de batch vão mudar isso: uma operação ruim aborta o batch. O provider pre-11 teria disparado cada request independentemente. Valide seu error handling antes de levar o upgrade pra produção, e use AutoTransactionBehavior.Never se precisar das semânticas antigas.
Combinado com o novo modo de execução bulk e o suporte first-class a complex types no provider Cosmos, EF Core 11 é a primeira release onde a experiência Cosmos parece no mesmo nível dos providers relacionais pra workloads write-heavy. O upgrade é mecânico, o default é mais seguro, e as economias de RU em workloads partition-aligned são imediatas.