Condividi tramite


Valori generati

Le colonne di database possono avere i valori generati in diversi modi: le colonne chiave primaria spesso incrementano automaticamente numeri interi, altre colonne hanno valori predefiniti o calcolati e così via. Questa pagina illustra in dettaglio vari modelli per la generazione di valori di configurazione con EF Core.

Valori predefiniti

Nei database relazionali è possibile configurare una colonna con un valore predefinito; se una riga viene inserita senza un valore per tale colonna, verrà utilizzato il valore predefinito.

È possibile configurare un valore predefinito in una proprietà:

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

È anche possibile specificare un frammento SQL usato per calcolare il valore predefinito:

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

Colonne calcolate

Nella maggior parte dei database relazionali è possibile configurare una colonna in modo che il relativo valore venga calcolato nel database, in genere con un'espressione che fa riferimento ad altre colonne:

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

Nell'esempio precedente viene creata una colonna calcolata virtuale , il cui valore viene calcolato ogni volta che viene recuperato dal database. È anche possibile specificare che una colonna calcolata venga archiviata (talvolta chiamata persistente), ovvero calcolata in ogni aggiornamento della riga e archiviata su disco insieme a colonne regolari:

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

Chiavi primarie

Per convenzione, le chiavi primarie non composite di tipo short, int, long o Guid vengono configurate per avere valori generati per le entità inserite se un valore non viene fornito dall'applicazione. Il provider di database si occupa in genere della configurazione necessaria; Ad esempio, una chiave primaria numerica in SQL Server viene configurata automaticamente come colonna IDENTITY.

Per altre informazioni, vedere la documentazione sulle chiavi e le linee guida per strategie di mapping di ereditarietà specifiche.

Configurazione esplicita della generazione di valori

Si è visto sopra che EF Core configura automaticamente la generazione di valori per le chiavi primarie, ma potrebbe essere opportuno eseguire la stessa operazione per le proprietà non chiave. È possibile configurare qualsiasi proprietà in modo che venga generato il relativo valore per le entità inserite come indicato di seguito:

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

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

Analogamente, è possibile configurare una proprietà in modo che il valore venga generato all'aggiunta o all'aggiornamento:

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

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

A differenza dei valori predefiniti o delle colonne calcolate, non viene specificata la modalità di generazione dei valori, che dipende dal provider di database in uso. I provider di database possono configurare automaticamente la generazione di valori per alcuni tipi di proprietà, ma altri potrebbero richiedere la configurazione manuale della modalità di generazione del valore.

Ad esempio, in SQL Server, quando una proprietà GUID è configurata come chiave primaria, il provider esegue automaticamente il lato client di generazione di valori, usando un algoritmo per generare valori GUID sequenziali ottimali. Tuttavia, la specifica in ValueGeneratedOnAdd una proprietà DateTime non avrà alcun effetto (vedere la sezione seguente per la generazione di valori DateTime).

Analogamente, le proprietà byte[] configurate come generate in caso di aggiunta o aggiornamento e contrassegnate come token di concorrenza vengono configurate con il tipo di dati rowversion, in modo che i valori vengano generati automaticamente nel database. Tuttavia, l'impostazione ValueGeneratedOnAdd non ha alcun effetto.

Consultare la documentazione del provider per le tecniche di generazione di valori specifiche supportate. La documentazione sulla generazione di valori di SQL Server è disponibile qui.

Generazione di valori di data/ora

Una richiesta comune consiste nell'avere una colonna di database che contiene la data/ora per la prima volta in cui la riga è stata inserita (valore generato al momento dell'aggiunta) o per quando è stata aggiornata per l'ultimo aggiornamento (valore generato al momento dell'aggiunta o dell'aggiornamento). Poiché esistono diverse strategie per eseguire questa operazione, i provider EF Core in genere non configurano automaticamente la generazione di valori per le colonne di data/ora. È necessario configurarlo manualmente.

Timestamp di creazione

La configurazione di una colonna di data/ora in modo che il timestamp di creazione della riga sia in genere una questione di configurazione di un valore predefinito con la funzione SQL appropriata. Ad esempio, in SQL Server è possibile usare quanto segue:

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

Assicurarsi di selezionare la funzione appropriata, in quanto potrebbero esistere diversi (ad esempio GETDATE() , rispetto GETUTCDATE()a ).

Timestamp dell'aggiornamento

Anche se le colonne calcolate archiviate sembrano una buona soluzione per la gestione dei timestamp aggiornati dell'ultimo aggiornamento, i database in genere non consentono di specificare funzioni come GETDATE() in una colonna calcolata. In alternativa, è possibile configurare un trigger di database per ottenere lo stesso effetto:

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

Per informazioni sulla creazione di trigger, vedere la documentazione sull'uso di SQL non elaborato nelle migrazioni.

Override della generazione di valori

Anche se una proprietà è configurata per la generazione di valori, in molti casi è comunque possibile specificare in modo esplicito un valore. Se questo funzionerà effettivamente dipende dal meccanismo di generazione di valori specifico configurato; anche se è possibile specificare un valore esplicito invece di usare il valore predefinito di una colonna, non è possibile eseguire la stessa operazione con le colonne calcolate.

Per eseguire l'override della generazione di valori con un valore esplicito, è sufficiente impostare la proprietà su qualsiasi valore che non sia il valore predefinito CLR per il tipo di tale proprietà (null per string, 0 per int, Guid.Empty per Guide così via).

Nota

Il tentativo di inserimento di valori espliciti in SQL Server IDENTITY ha esito negativo per impostazione predefinita; vedere questi documenti per una soluzione alternativa.

Per fornire un valore esplicito per le proprietà configurate come valore generato in caso di aggiunta o aggiornamento, è necessario configurare la proprietà come indicato di seguito:

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

Nessuna generazione di valori

Oltre a scenari specifici, ad esempio quelli descritti in precedenza, le proprietà in genere non hanno alcuna generazione di valori configurata; ciò significa che spetta all'applicazione specificare sempre un valore da salvare nel database. Questo valore deve essere assegnato alle nuove entità prima di essere aggiunte al contesto.

Tuttavia, in alcuni casi può essere necessario disabilitare la generazione di valori configurata per convenzione. Ad esempio, una chiave primaria di tipo int viene in genere configurata in modo implicito come add-generated-on-value (ad esempio, colonna Identity in SQL Server). È possibile disabilitare questa operazione tramite gli elementi seguenti:

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

    public string Url { get; set; }
}