Database

Entity Framework Core 7 columnas JSON

En este artículo, mostraré una breve introducción sobre EF Core 7 y las columnas JSON y también un ejemplo práctico que permite la operación de mapeo, consulta y actualización de datos JSON.

La compatibilidad con columnas JSON permite que los sistemas de bases de datos relacionales adopten algunas de las características de los sistemas de bases de datos de documentos.

El JSON en estas columnas se puede profundizar con consultas. Esto permite, por ejemplo, filtrar y ordenar por elementos específicos del documento, así como la proyección de elementos del documento en los resultados.

EF7 tiene soporte para columnas JSON independiente del proveedor, con una implementación para SQL Server. Este soporte permite el mapeo de agregados (creados a partir de tipos .NET) a documentos JSON.

Las consultas LINQ estándar se pueden usar en los agregados y se convertirán en las construcciones de consulta adecuadas necesarias para profundizar en el JSON.

Además, EF7 permite actualizar y almacenar cambios en documentos JSON.

Veamos el código👇

Mapeo a columnas JSON

En EF Core, los tipos agregados se definen mediante «OwnsOne» y «OwnsMany». Pongamos el ejemplo de Address para este ejemplo.

El tipo de agregado se configura «OnModelCreating» usando OwnsOne:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    //User
    ...
    modelBuilder.Entity<User>().OwnsOne(b => b.Address);
    ...
}

De forma predeterminada, los proveedores de bases de datos relacionales de EF Core asignan tipos agregados como este a la misma tabla que el tipo de entidad propietaria. Es decir, cada propiedad de las clases de «Address» se asigna a una columna de la tabla «User»:

Si lo deseamos, cada tipo de entidad que forma el agregado se puede asignar a su propia tabla usando ToTable:

//User
...
modelBuilder.Entity<User>()
    .OwnsOne(b => b.Address, ownedNavigationBuilder =>
    {
        ownedNavigationBuilder.ToTable("Address");
    });

...

Con este cambio se genera una nueva tabla llamada «Address»:

Por último, veamos la nueva e interesante característica de EF7, el agregado Address se puede asignar a una columna JSON. Esto requiere solo una llamada al configurar el tipo agregado: ToJson()

//User
...
modelBuilder.Entity<User>()
    .OwnsOne(b => b.Address, ownedNavigationBuilder =>
    {
        ownedNavigationBuilder.ToJson();
    });

...

La tabla «User» ahora contendrá una columna JSON para «Address», en la que guardaremos un documento JSON para cada usuario:

Las columnas JSON brindan las capacidades de usar EF Core contra bases de datos de documentos a documentos incrustados en una base de datos relacional.

Los documentos JSON que se muestran arriba son muy simples, pero esta capacidad de mapeo también se puede usar con estructuras de documentos más complejas.

Consulta a columnas JSON

Las consultas en columnas JSON funcionan igual que las consultas en cualquier otro tipo de agregado en EF Core.

var user = await context.Users
    .Where(user => user.Address.City == "Madrid")
    .ToListAsync();

En este ejemplo hace un poco más en el filtro y la proyección, y también ordena por correo electrónico en el documento JSON:

var orderedAddresses = await context.Users
    .Where(
        user => (user.Address.City == "Chigley"
                   && user.Email != null)
                  || user.Name.StartsWith("D"))
    .OrderBy(user => user.Email)
    .Select(
        user => user.Name + " (" + user.Address.Street
                  + ", " + user.Address.City
                  + " " + user.Address.Postcode + ")")
    .ToListAsync();

SUGERENCIA Considere la posibilidad de crear índices para mejorar el rendimiento de las consultas en documentos JSON. Por ejemplo, consulte Indexar datos Json al usar SQL Server.

Actualización de columnas JSON

Se usa SaveChanges y SaveChangesAsync de la manera normal para hacer actualizaciones en una columna JSON. Para cambios extensos, se actualizará todo el documento.

var user = await context.Users.SingleAsync(u => u.Name.StartsWith("Alberto"));

user.Contact = new() { Address = new("2 Riverside", "Trimbridge", "TB1 5ZS", "UK"), Phone = "01632 88346" };

await context.SaveChangesAsync();

Sin embargo, si solo se cambia un subdocumento, EF Core usará un comando «JSON_MODIFY» para actualizar solo el subdocumento. Por ejemplo, cambiando el interior de «Address» de un documento «Contact»:

var user = await context.Users.SingleAsync(u => u.Name.StartsWith("Luis"));

user.Contact.Address = new("4 Riverside", "Trimbridge", "TB1 5ZS", "UK");

await context.SaveChangesAsync();

Finalmente, si solo se cambia una sola propiedad, EF Core volverá a usar un comando «JSON_MODIFY», esta vez para parchear solo el valor de la propiedad modificada. Por ejemplo:

var user = await context.Authors.SingleAsync(u => u.Name.StartsWith("Antonio"));

user.Contact.Address.Country = "España";

await context.SaveChangesAsync();

Resumen

EF Core 7.0 (EF7) agregó compatibilidad para asignar tipos agregados a documentos JSON guardados en «columnas JSON» de la base de datos relacional. Como resultado, las bases de datos relacionales pueden almacenar documentos directamente mientras mantienen la estructura relacional general de los datos. Las columnas JSON son compatibles con EF7 con una implementación para SQL Server que es independiente del proveedor. Es posible consultar el JSON incluido en estas columnas mediante LINQ, que permite filtrar y ordenar por elemento del documento, así como la proyección del elemento del documento en los resultados. Además, cuando se invoca SaveChanges, EF7 permite actualizaciones parciales solo para los elementos alterados y el seguimiento de cambios a nivel de elemento de los documentos.

Eso es todo, por el momento, seguiré actualizando y agregando contenido en este post. Espero que te haya resultado interesante😉

Descargas

El código fuente de este artículo se puede encontrar en GitHub

1