← maurobernal.com.ar

Novedades en EF Core 11: Vector Search, Cosmos DB y migraciones mejoradas

EF Core 11 (EF11) es la siguiente versión después de EF Core 10 y está actualmente en desarrollo junto con .NET 11. Esta versión trae mejoras importantes en herencia, soporte para Vector Search en SQL Server 2025, Cosmos DB más potente y un flujo de migraciones mucho más cómodo.

🧩 Complex Types y JSON Columns en TPT/TPC

Hasta ahora, los complex types y las JSON columns no podían usarse en entidades con herencia TPT (table-per-type) o TPC (table-per-concrete-type). EF Core 11 elimina esa limitación.

Ejemplo con herencia TPT y un complex type:

public abstract class Animal
{
    public int Id { get; set; }
    public string Name { get; set; }
    public required AnimalDetails Details { get; set; }
}

public class Dog : Animal { public string Breed { get; set; } }
public class Cat : Animal { public bool IsIndoor { get; set; } }

[ComplexType]
public class AnimalDetails
{
    public DateTime BirthDate { get; set; }
    public string? Veterinarian { get; set; }
}

EF 11 genera correctamente las columnas Details_BirthDate y Details_Veterinarian en la tabla Animal. También podés mapear el complex type a una JSON column:

modelBuilder.Entity<Animal>()
    .UseTptMappingStrategy()
    .ComplexProperty(a => a.Details, b => b.ToJson());

🔍 LINQ: MaxBy y MinBy

EF Core 11 traduce MaxByAsync y MinByAsync a SQL. Permiten obtener la entidad con el valor máximo o mínimo de un campo calculado:

var blogWithMostPosts = await context.Blogs.MaxByAsync(b => b.Posts.Count());

Se traduce a:

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

☁️ Cosmos DB: batches, bulk execution y session tokens

Transactional Batches

EF Core 11 aprovecha los transactional batches de Cosmos DB por defecto, agrupando operaciones por container y partición para ejecutarlas atómicamente en un solo roundtrip. El comportamiento se controla con AutoTransactionBehavior:

  • Auto (default): operaciones agrupadas en batches por container/partición.
  • Never: operaciones individuales y secuenciales (comportamiento pre-EF11).
  • Always: exige que todo entre en un único batch; lanza excepción si no es posible.

Bulk Execution

Habilitá la ejecución masiva en paralelo para mejorar el throughput al guardar muchas entidades:

optionsBuilder.UseCosmos(
    "<connection string>",
    databaseName: "OrdersDB",
    options => options.BulkExecutionEnabled());

Session Token Management

Para entornos con múltiples instancias (por ejemplo, load balancing round-robin), EF Core 11 expone APIs para gestionar manualmente los session tokens y garantizar consistencia read-your-writes:

var sessionToken = context.Database.GetSessionToken();

context.Database.UseSessionToken(sessionToken);
var result = await context.Documents.FindAsync(id);

🗄️ Migraciones mejoradas

Crear y aplicar en un solo paso

El nuevo flag --add en dotnet ef database update crea y aplica una migración en un único paso usando Roslyn. Ideal para .NET Aspire y apps containerizadas:

dotnet ef database update InitialCreate --add
dotnet ef database update AddProducts --add --output-dir Migrations/Products --namespace MyApp.Migrations

En PowerShell:

Update-Database -Migration InitialCreate -Add

Connection y modo offline para migrations remove

Los comandos migrations remove y database drop ahora aceptan --connection. Además, --offline permite eliminar migraciones sin conectarse a la base de datos:

dotnet ef migrations remove --connection "Server=prod;Database=MyDb;..."
dotnet ef migrations remove --offline
dotnet ef migrations remove --force

🔎 SQL Server: Vector Search e índices vectoriales

EF Core 11 añade soporte para VECTOR_SEARCH() e índices vectoriales en SQL Server 2025. Definís el índice en el modelo:

modelBuilder.Entity<Blog>()
    .HasVectorIndex(b => b.Embedding, "cosine");

Y realizás búsquedas aproximadas en LINQ:

var blogs = await context.Blogs
    .VectorSearch(b => b.Embedding, "cosine", embedding, topN: 5)
    .ToListAsync();

VectorSearch() devuelve VectorSearchResult<TEntity> con acceso al score de distancia junto a la entidad.

📄 SQL Server: Full-Text Search mejorado

Catálogos e índices desde el modelo

Configurá catálogos e índices full-text directamente en tu modelo para que las migraciones los gestionen automáticamente:

modelBuilder.HasFullTextCatalog("ftCatalog");

modelBuilder.Entity<Blog>()
    .HasFullTextIndex(b => b.FullName)
    .HasKeyIndex("PK_Blogs")
    .OnCatalog("ftCatalog");

FREETEXTTABLE y CONTAINSTABLE con ranking

EF Core 11 soporta las funciones table-valued que devuelven un score de relevancia:

var results = await context.Blogs
    .FreeTextTable(b => b.FullName, "John")
    .Select(r => new { Blog = r.Value, Rank = r.Rank })
    .OrderByDescending(r => r.Rank)
    .ToListAsync();

⚡ SQL Server 2025: JSON_CONTAINS

Cuando apuntás a SQL Server 2025 (compatibility level 170), las queries Contains sobre colecciones JSON usan la nueva función JSON_CONTAINS(), reemplazando el menos eficiente OPENJSON y permitiendo aprovechar índices JSON:

optionsBuilder.UseSqlServer("<CONNECTION STRING>", o => o.UseCompatibilityLevel(170));

// EF genera: WHERE JSON_CONTAINS([b].[Tags], 'ef-core') = 1
var blogs = await context.Blogs
    .Where(b => b.Tags.Contains("ef-core"))
    .ToListAsync();

📝 Conclusión

EF Core 11 cierra brechas importantes: herencia con complex types y JSON columns, LINQ más expresivo con MaxBy/MinBy, Cosmos DB listo para escala en producción, migraciones más ergonómicas y soporte de primera clase para SQL Server 2025 con Vector Search, JSON_CONTAINS y full-text con ranking.

Para ver todas las novedades, la documentación oficial está en learn.microsoft.com/ef/core/what-is-new/ef-core-11.0/whatsnew.