Valores gerados

As colunas de banco de dados podem ter seus valores gerados de várias maneiras: colunas de chave primária são frequentemente inteiros de incremento automático, outras colunas têm valores padrão ou calculados. Esta página detalha vários padrões de geração de valor de configuração com o EF Core.

Valores padrão

Em bancos de dados relacionais, uma coluna pode ser configurada com um valor padrão; se uma linha for inserida sem um valor para essa coluna, o valor padrão será usado.

Você pode configurar um valor padrão em uma propriedade:

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

Você também pode especificar um fragmento SQL usado para calcular o valor padrão:

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

Colunas computadas

Na maioria dos bancos de dados relacionais, uma coluna pode ser configurada para ter seu valor calculado no banco de dados, normalmente com uma expressão que se refere a outras colunas:

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

O anterior cria uma coluna computada virtual, cujo valor é calculado sempre que é obtido do banco de dados. Você também pode especificar que uma coluna computada seja armazenada (às vezes chamada mantida), o que significa que ela é calculada em cada atualização da linha e é armazenada no disco junto com colunas regulares:

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

Chaves primárias

Por convenção, chaves primárias não compostas do tipo short, int, long ou Guid são configuradas para ter valores gerados para entidades inseridas se um valor não for fornecido pelo aplicativo. Seu provedor de banco de dados normalmente cuida da configuração necessária; por exemplo, uma chave primária numérica no SQL Server é configurada automaticamente para ser uma coluna IDENTITY.

Para obter mais informações, consulte a documentação sobre chaves e as diretrizes para estratégias de mapeamento de herança específicas.

Configurar a geração de valor explicitamente

Vimos acima que o EF Core configura automaticamente a geração de valor para chaves primárias, mas talvez queiramos fazer o mesmo para propriedades não chave. Você pode configurar qualquer propriedade para ter seu valor gerado para entidades inseridas da seguinte maneira:

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

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

Da mesma forma, uma propriedade pode ser configurada para ter seu valor gerado na adição ou atualização:

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

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

Ao contrário de valores padrão ou colunas computadas, não estamos especificando como os valores devem ser gerados; isso depende do provedor de banco de dados que está sendo usado. Os provedores de banco de dados podem configurar automaticamente a geração de valor para alguns tipos de propriedade, mas outros podem exigir que você configure manualmente como o valor é gerado.

Por exemplo, no SQL Server, quando uma propriedade GUID é configurada como uma chave primária, o provedor executa automaticamente a geração de valor do lado do cliente, usando um algoritmo para gerar valores de GUID sequenciais ideais. No entanto, especificar ValueGeneratedOnAdd em uma propriedade DateTime não terá efeito (consulte a seção abaixo sobre geração de valor DateTime).

Da mesma forma, as propriedades byte[] configuradas como geradas na adição ou atualização e marcadas como tokens de simultaneidade são configuradas com o tipo de dados rowversion, para que os valores sejam gerados automaticamente no banco de dados. No entanto, especificar ValueGeneratedOnAdd não tem efeito.

Consulte a documentação do provedor para obter as técnicas específicas de geração de valor compatíveis. A documentação de geração de valor do SQL Server pode ser encontrada aqui.

Geração de valor de data/hora

Uma solicitação comum é ter uma coluna de banco de dados que contenha a data/hora de quando a linha foi inserida pela primeira vez (valor gerado na adição) ou quando ela foi atualizada pela última vez (valor gerado na adição ou atualização). Como há várias estratégias para fazer isso, os provedores do EF Core geralmente não configuram a geração de valor automaticamente para colunas de data/hora. Você mesmo precisa configurar isso.

Carimbo de data/hora de criação

Configurar uma coluna de data/hora para ter o carimbo de data/hora de criação da linha geralmente é uma questão de configurar um valor padrão com a função SQL apropriada. Por exemplo, no SQL Server, você pode usar o seguinte:

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

Selecione a função apropriada, pois podem existir várias (por exemplo, GETDATE() vs. GETUTCDATE()).

Carimbo de data/hora de atualização

Embora as colunas computadas armazenadas pareçam uma boa solução para gerenciar os últimos carimbos de data/hora atualizados, os bancos de dados geralmente não permitem especificar funções como GETDATE() em uma coluna computada. Como alternativa, você pode configurar um gatilho de banco de dados para obter o mesmo efeito:

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 obter informações sobre como criar gatilhos, consulte a documentação sobre como usar o SQL bruto em migrações.

Substituir a geração de valor

Embora uma propriedade esteja configurada para geração de valor, em muitos casos ainda é possível especificar explicitamente um valor para ela. Se isso realmente funcionará depende do mecanismo de geração de valor específico que foi configurado; embora você possa especificar um valor explícito em vez de usar o valor padrão de uma coluna, o mesmo não pode ser feito com colunas computadas.

Para substituir a geração de valor por um valor explícito, basta definir a propriedade como qualquer valor que não seja o valor padrão CLR para o tipo dessa propriedade (null para string, 0 para int, Guid.Empty para Guid, etc.).

Observação

A tentativa de inserir valores explícitos em IDENTITY no SQL Server falha por padrão; consulte esses documentos para obter uma solução alternativa.

Para fornecer um valor explícito para propriedades que foram configuradas como valor gerado na adição ou atualização, você também deve configurar a propriedade da seguinte maneira:

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

Sem geração de valor

Além de cenários específicos, como os descritos acima, as propriedades normalmente não têm nenhuma geração de valor configurada; isso significa que cabe ao aplicativo sempre fornecer um valor a ser salvo no banco de dados. Esse valor deve ser atribuído a novas entidades antes que elas sejam adicionadas ao contexto.

No entanto, em alguns casos, talvez você queira desabilitar a geração de valor que foi configurada por convenção. Por exemplo, uma chave primária do tipo int geralmente é configurada implicitamente como value-generated-on-add (por exemplo, coluna de identidade no SQL Server). Você pode desabilitar isso por meio do seguinte:

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

    public string Url { get; set; }
}