Partilhar via


Valores gerados

As colunas do 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 computados, etc. Esta página detalha vários padrões para 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:

modelBuilder.Entity<Blog>()
    .Property(b => b.Rating)
    .HasDefaultValue(3);

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

modelBuilder.Entity<Blog>()
    .Property(b => b.Created)
    .HasDefaultValueSql("getdate()");

O valor padrão também entra em vigor ao adicionar uma nova coluna a uma tabela; As linhas existentes que ainda não têm um valor para a nova coluna terão o valor padrão definido nelas.

Nome da restrição de valor padrão

A partir do EF 10, para o SQL Server você pode especificar explicitamente o nome para restrições de valor padrão, oferecendo mais controle sobre seu esquema de banco de dados.

modelBuilder.Entity<Blog>()
    .Property(b => b.Rating)
    .HasDefaultValue(3, "DF_Blog_IsActive");
modelBuilder.Entity<Blog>()
    .Property(b => b.Created)
    .HasDefaultValueSql("getdate()" , "DF_Blog_IsActive");

Pode também contactar UseNamedDefaultConstraints para ativar a nomeação automática de todas as restrições padrão. Observe que, se você tiver migrações existentes, a próxima migração adicionada renomeará todas as restrições padrão em seu modelo.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.UseNamedDefaultConstraints();
}

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 referente a outras colunas:

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

O acima cria uma coluna virtual computada, cujo valor é calculado toda vez que é buscado no banco de dados. Você também pode especificar que uma coluna computada seja armazenada (às vezes chamada de persistente), o que significa que ela é calculada em cada atualização da linha e é armazenada no disco ao lado de 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 orientações para estratégias específicas de mapeamento de herança.

Configurando explicitamente a geração de valor

Vimos acima que o EF Core configura automaticamente a geração de valor para chaves primárias - mas podemos querer 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 ao adicionar ou atualizar:

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

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

Ao contrário dos 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 GUID sequenciais ideais. No entanto, especificar ValueGeneratedOnAdd em uma propriedade DateTime não terá efeito (consulte a seção abaixo para geração de valor DateTime).

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

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

Geração de valores 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 ao adicionar) ou de quando foi atualizada pela última vez (valor gerado em adicionar ou atualizar). Como existem 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.

Data e hora de criação

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

modelBuilder.Entity<Blog>()
    .Property(b => b.Created)
    .HasDefaultValueSql("getdate()");

Certifique-se de selecionar a função apropriada, pois várias podem existir (por exemplo GETDATE() , vs. GETUTCDATE()).

Atualizar carimbo de data/hora

Embora as colunas computadas armazenadas pareçam uma boa solução para gerenciar carimbos de data/hora atualizados pela última vez, 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 SQL bruto em migrações.

Sobrepondo-se à geração de valor

Embora uma propriedade esteja configurada para geração de valor, em muitos casos você ainda pode especificar explicitamente um valor para ela. Se isso realmente funcionará depende do mecanismo específico de geração de valor 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 for string, 0 for int, Guid.Empty for Guid, etc.).

Observação

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

Para fornecer um valor explícito para propriedades que foram configuradas como valor gerado ao adicionar ou atualizar, 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 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, você pode querer desativar a geração de valor que foi configurada por convenção. Por exemplo, uma chave primária do tipo int geralmente é implicitamente configurada como value-generated-on-add (por exemplo, coluna de identidade no SQL Server). Você pode desativar isso através do seguinte:

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

    public string Url { get; set; }
}