Valores generados
Las columnas de base de datos pueden generar sus valores de varias maneras: las columnas de clave principal suelen ser enteros de incremento automático, 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, una columna se puede configurar 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 SQL que se usa 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 se almacene una columna calculada (a veces denominada persistente),lo que significa que se calcula en cada actualización de la fila y se almacena en el disco junto con columnas normales:
modelBuilder.Entity<Person>()
.Property(p => p.NameLength)
.HasComputedColumnSql("LEN([LastName]) + LEN([FirstName])", stored: true);
Nota
Se agregó compatibilidad para crear columnas calculadas almacenadas en EF Core 5.0.
Claves principales
Por convención, las claves principales no compuestas de tipo short, int, long o Guid se establecen para que se generen valores para las entidades insertadas si la aplicación no proporciona un valor. Normalmente, el proveedor de bases de datos 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 obtener más información, consulte la documentación sobre claves.
Configuración explícita de la generación de valores
Vimos anteriormente que EF Core automáticamente la generación de valores para las claves principales, pero es posible que deseemos hacer lo mismo para 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; }
}
De forma similar, una propiedad se puede configurar para que su valor se genere al agregar o actualizar:
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime LastUpdated { get; set; }
}
Advertencia
A diferencia de los valores predeterminados o las columnas calculadas, no se especifica cómo se van a generar los valores. que depende del proveedor de base de datos que se esté utilizando. Los proveedores de bases de datos pueden configurar automáticamente la generación de valores para algunos tipos de propiedad, pero otros pueden requerir que configure manualmente cómo se genera el valor.
Por ejemplo, en SQL Server, cuando una propiedad GUID se configura como valor generado al agregar, el proveedor realiza automáticamente la generación de valores en el lado cliente, mediante un algoritmo para generar valores GUID secuenciales óptimos. Sin embargo, especificar ValueGeneratedOnAdd en una propiedad DateTime no tendrá ningún efecto (vea la sección siguiente para la generaciónde valores DateTime).
Del mismo modo, las propiedades byte[] configuradas como generadas al agregar o actualizar y marcadas 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.
Nota
En función del proveedor de base de datos que se esté utilizando, EF o la base de datos pueden generar valores en el lado cliente. Si la base de datos genera el valor, EF puede asignar un valor temporal al agregar la entidad al contexto; Este valor temporal se reemplazará por el valor generado por la base de datos durante SaveChanges() . Para obtener más información, vea los documentos sobre valores temporales.
Generación de valores de fecha y hora
Una solicitud común es tener una columna de base de datos que contenga la fecha y hora de la primera inserción de la columna (valor generado al agregar) o de cuándo se actualizó por última vez (valor generado al agregar o actualizar). Como hay varias estrategias para hacerlo, EF Core los proveedores no suelen configurar automáticamente la generación de valores para las columnas de fecha y hora; tiene que configurarlo usted mismo.
Marca de tiempo de creación
La configuración de una columna de fecha y hora para que tenga la marca de tiempo de creación de la fila suele ser una 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() ).
Actualizar marca de tiempo
Aunque las columnas calculadas almacenadas parecen una buena solución para administrar las marcas de tiempo de última actualización, las bases de datos normalmente no permiten especificar funciones como en una GETDATE() 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;
DECLARE @Id INT
SELECT @Id = INSERTED.BlogId
FROM INSERTED
UPDATE dbo.Blogs
SET LastUpdated = GETDATE()
WHERE BlogId = @Id
END
Para obtener información sobre cómo crear 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 todavía puede especificar explícitamente un valor para ella. Si esto realmente funcionará depende del mecanismo de generación de valores específico que se haya 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, basta con establecer la propiedad en cualquier valor que no sea el valor predeterminado de CLR para el tipo de esa propiedad ( para null , para , para , string0intGuid.EmptyGuid etc.).
Nota
Al intentar insertar valores explícitos en SQL Server IDENTITY 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 valor generado al agregar o actualizar, 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 que se va a guardar en la base de datos. Este valor debe asignarse a nuevas entidades antes de agregarse al contexto.
Sin embargo, en algunos casos puede que desee deshabilitar la generación de valores que se ha configurado por convención. Por ejemplo, una clave principal de tipo int normalmente se configura implícitamente como value-generated-on-add (por ejemplo, columna de identidad en SQL Server). Puede deshabilitarlo a través de lo siguiente:
public class Blog
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int BlogId { get; set; }
public string Url { get; set; }
}