Valores generados

Los valores de las columnas de base de datos se pueden generar de varias maneras: las columnas de clave principal suelen incrementar automáticamente los enteros, otras columnas tienen valores predeterminados o calculados, etc. En esta página se detallan varios patrones para la generación de valores de configuración con EF Core.

Valores predeterminados

En las bases de datos relacionales, se puede configurar una columna con un valor predeterminado; si se inserta una fila sin un valor para esa columna, se usará el valor predeterminado.

Puede configurar un valor predeterminado en una propiedad:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Property(b => b.Rating)
        .HasDefaultValue(3);
}

También puede especificar un fragmento de SQL para calcular el valor predeterminado:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Property(b => b.Created)
        .HasDefaultValueSql("getdate()");
}

Columnas calculadas

En la mayoría de las bases de datos relacionales, se puede configurar una columna para que su valor se calcule en la base de datos, normalmente con una expresión que haga referencia a otras columnas:

modelBuilder.Entity<Person>()
    .Property(p => p.DisplayName)
    .HasComputedColumnSql("[LastName] + ', ' + [FirstName]");

Lo anterior crea una columna calculada virtual, cuyo valor se calcula cada vez que se captura de la base de datos. También puede especificar que una columna calculada se almacene (o se conserve), lo que significa que se calcula con cada actualización de la fila y se almacena en el disco junto a las columnas normales:

modelBuilder.Entity<Person>()
    .Property(p => p.NameLength)
    .HasComputedColumnSql("LEN([LastName]) + LEN([FirstName])", stored: true);

Claves principales

Por convención, las claves principales no compuestas de tipo short, int, long o Guid están configuradas para que sus valores se generen para las entidades insertadas si la aplicación no proporciona ningún valor. El proveedor de bases de datos normalmente se encarga de la configuración necesaria; por ejemplo, una clave principal numérica en SQL Server se configura automáticamente para que sea una columna IDENTITY.

Para más información, consulte la documentación sobre las claves y la guía sobre las estrategias específicas de asignación de herencia.

Configuración explícita de la generación de valores

Hemos visto anteriormente que EF Core configura automáticamente la generación de valores para las claves principales, pero es posible que deseemos hacer lo mismo con las propiedades que no son de clave. Puede configurar cualquier propiedad para que su valor se genere para las entidades insertadas de la siguiente manera:

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public DateTime Inserted { get; set; }
}

Del mismo modo, se puede configurar una propiedad para que su valor se genere al agregarla o actualizarla:

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
    public DateTime LastUpdated { get; set; }
}

A diferencia de los valores predeterminados o las columnas calculadas, no se especifica cómo se van a generar los valores, lo que depende del proveedor de bases de datos que se use. Los proveedores de bases de datos pueden configurar automáticamente la generación de valores para algunos tipos de propiedades, pero otros pueden requerir que se configure manualmente cómo se genera el valor.

Por ejemplo, en SQL Server, cuando una propiedad GUID está configurada como clave principal, el proveedor realiza automáticamente la generación de valores del lado cliente mediante un algoritmo para generar valores GUID secuenciales óptimos. Sin embargo, especificar ValueGeneratedOnAdd en una propiedad DateTime no tendrá ningún efecto (consulte la sección siguiente sobre la generación de valores DateTime).

Del mismo modo, las propiedades byte[] configuradas como que se generan cuando se agregan o actualizan y se marcan como tokens de simultaneidad se configuran con el tipo de datos rowversion, de modo que los valores se generan automáticamente en la base de datos. Sin embargo, especificar ValueGeneratedOnAdd no tiene ningún efecto.

Consulte la documentación de su proveedor sobre las técnicas específicas de generación de valores que admite. La documentación de generación de valores de SQL Server se puede encontrar aquí.

Generación de valores de fecha y hora

Una solicitud común es tener una columna de base de datos que contenga la fecha/hora de cuando la fila fue insertada por primera vez (valor generado al agregarla), o de cuando se actualizó por última vez (valor generado al agregarla o actualizarla). Como hay varias estrategias para hacerlo, los proveedores de EF Core normalmente no configuran automáticamente la generación de valores para las columnas de fecha y hora, tiene que configurarla por su cuenta.

Marca de tiempo de creación

Configurar una columna de fecha/hora para que tenga la marca de tiempo de creación de la fila suele ser cuestión de configurar un valor predeterminado con la función SQL adecuada. Por ejemplo, en SQL Server puede usar lo siguiente:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Property(b => b.Created)
        .HasDefaultValueSql("getdate()");
}

Asegúrese de seleccionar la función adecuada, ya que pueden existir varias (por ejemplo, GETDATE() frente a GETUTCDATE()).

Marca de tiempo de actualización

Aunque las columnas calculadas almacenadas parecen una buena solución para administrar las marcas de tiempo actualizadas por última vez, las bases de datos normalmente no permiten especificar funciones como GETDATE() en una columna calculada. Como alternativa, puede configurar un desencadenador de base de datos para lograr el mismo efecto:

CREATE TRIGGER [dbo].[Blogs_UPDATE] ON [dbo].[Blogs]
    AFTER UPDATE
AS
BEGIN
    SET NOCOUNT ON;

    IF ((SELECT TRIGGER_NESTLEVEL()) > 1) RETURN;

    UPDATE B
    SET LastUpdated = GETDATE()
    FROM dbo.Blogs AS B
    INNER JOIN INSERTED AS I
        ON B.BlogId = I.BlogId
END

Para información sobre la creación de desencadenadores, consulte la documentación sobre el uso de SQL sin procesar en las migraciones.

Invalidación de la generación de valores

Aunque una propiedad está configurada para la generación de valores, en muchos casos puede especificar explícitamente un valor para ella. Que esto funcione realmente depende del mecanismo de generación de valores específico que se ha configurado; aunque puede especificar un valor explícito en lugar de usar el valor predeterminado de una columna, no se puede hacer lo mismo con las columnas calculadas.

Para invalidar la generación de valores con un valor explícito, simplemente establezca la propiedad en cualquier valor que no sea el valor predeterminado de CLR para el tipo de esa propiedad (null para string, 0 para int, Guid.Empty para Guid, etc.).

Nota:

Al intentar insertar valores explícitos en IDENTITY de SQL Server, se produce un error de forma predeterminada; consulte estos documentos para obtener una solución alternativa.

Para proporcionar un valor explícito para las propiedades que se han configurado como que se generan al agregarse o actualizarse, también debe configurar la propiedad de la siguiente manera:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>().Property(b => b.LastUpdated)
        .ValueGeneratedOnAddOrUpdate()
        .Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Save);
}

Sin generación de valores

Aparte de escenarios específicos, como los descritos anteriormente, las propiedades normalmente no tienen configurada la generación de valores; esto significa que la aplicación siempre debe proporcionar un valor para guardarse en la base de datos. Este valor se debe asignar a nuevas entidades antes de que se agreguen al contexto.

Sin embargo, en algunos casos, puede que desee deshabilitar la generación de valores configurada por convención. Por ejemplo, una clave principal de tipo int normalmente se configura implícitamente como value-generated-on-add (por ejemplo, la columna de identidad en SQL Server). Puede deshabilitar esta configuración de la manera siguiente:

public class Blog
{
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int BlogId { get; set; }

    public string Url { get; set; }
}