Valeurs générées

Les valeurs des colonnes de base de données peuvent être générées de différentes manières : les colonnes de clé primaire sont souvent des entiers à incrémentation automatique, les autres colonnes ont des valeurs par défaut ou calculées, etc. Cette page détaille différents modèles pour la génération de valeur de configuration avec EF Core.

Valeurs par défaut

Sur les bases de données relationnelles, une colonne peut être configurée avec une valeur par défaut ; si une ligne est insérée sans valeur pour cette colonne, la valeur par défaut est utilisée.

Vous pouvez configurer une valeur par défaut sur une propriété :

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

Vous pouvez également spécifier un fragment SQL utilisé pour calculer la valeur par défaut :

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

Colonnes calculées

Sur la plupart des bases de données relationnelles, une colonne peut être configurée pour que sa valeur soit calculée dans la base de données, généralement avec une expression faisant référence à d’autres colonnes :

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

L’élément ci-dessus crée une colonne calculée virtuelle , dont la valeur est calculée chaque fois qu’elle est extraite de la base de données. Vous pouvez également spécifier qu’une colonne calculée doit être stockée (parfois appelée persistante), ce qui signifie qu’elle est calculée sur chaque mise à jour de la ligne et qu’elle est stockée sur le disque avec les colonnes normales :

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

Clés primaires

Par convention, les clés primaires non composites de type short, int, long ou GUID sont configurées pour avoir des valeurs générées pour les entités insérées si aucune valeur n’est fournie par l’application. Votre fournisseur de base de données s’occupe généralement de la configuration nécessaire ; par exemple, une clé primaire numérique dans SQL Server est automatiquement configurée pour être une colonne IDENTITY.

Pour plus d’informations, consultez la documentation sur les clés et des conseils pour des stratégies de mappage d’héritage spécifiques.

Configuration explicite de la génération de valeur

Nous avons vu ci-dessus qu’EF Core configure automatiquement la génération de valeur pour les clés primaires, mais nous pouvons faire de même pour les propriétés autres que les clés. Vous pouvez configurer n’importe quelle propriété pour que sa valeur soit générée pour les entités insérées comme suit :

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

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

De même, une propriété peut être configurée pour que sa valeur soit générée lors de l’ajout ou de la mise à jour :

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

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

Contrairement aux valeurs par défaut ou aux colonnes calculées, nous ne spécifions pas comment les valeurs doivent être générées ; qui dépend du fournisseur de base de données utilisé. Les fournisseurs de base de données peuvent configurer automatiquement la génération de valeur pour certains types de propriétés, mais d’autres peuvent vous obliger à configurer manuellement la façon dont la valeur est générée.

Par exemple, sur SQL Server, lorsqu’une propriété GUID est configurée en tant que clé primaire, le fournisseur effectue automatiquement la génération de valeurs côté client, à l’aide d’un algorithme pour générer des valeurs DE GUID séquentielles optimales. Toutefois, la spécification d’une ValueGeneratedOnAdd propriété DateTime n’aura aucun effet (voir la section ci-dessous pour la génération de valeur DateTime).

De même, les propriétés byte[] configurées comme générées lors de l’ajout ou de la mise à jour et marquées comme jetons d’accès concurrentiel sont configurées avec le type de données rowversion, de sorte que les valeurs sont générées automatiquement dans la base de données. Toutefois, la spécification n’a ValueGeneratedOnAdd aucun effet.

Consultez la documentation de votre fournisseur pour connaître les techniques de génération de valeur spécifiques qu’il prend en charge. La documentation sur la génération de valeur SQL Server est disponible ici.

Génération de valeur de date/heure

Une requête courante consiste à avoir une colonne de base de données qui contient la date/heure de la première insertion de la colonne (valeur générée lors de l’ajout) ou de la date de la dernière mise à jour (valeur générée lors de l’ajout ou de la mise à jour). Comme il existe différentes stratégies pour ce faire, les fournisseurs EF Core ne configurent généralement pas automatiquement la génération de valeur pour les colonnes de date/heure. Vous devez configurer cela vous-même.

Horodatage de création

La configuration d’une colonne de date/heure pour avoir l’horodatage de création de la ligne consiste généralement à configurer une valeur par défaut avec la fonction SQL appropriée. Par exemple, sur SQL Server vous pouvez utiliser les éléments suivants :

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

Veillez à sélectionner la fonction appropriée, car plusieurs peuvent exister (par exemple, GETDATE() vs. GETUTCDATE()).

Horodatage de mise à jour

Bien que les colonnes calculées stockées semblent être une bonne solution pour gérer les derniers horodatages mis à jour, les bases de données n’autorisent généralement pas la spécification de fonctions telles que GETDATE() dans une colonne calculée. Vous pouvez également configurer un déclencheur de base de données pour obtenir le même effet :

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

Pour plus d’informations sur la création de déclencheurs, consultez la documentation sur l’utilisation de SQL brut dans les migrations.

Substitution de la génération de valeur

Bien qu’une propriété soit configurée pour la génération de valeur, dans de nombreux cas, vous pouvez toujours spécifier explicitement une valeur pour celle-ci. Le fait que cela fonctionne réellement dépend du mécanisme de génération de valeur spécifique qui a été configuré ; bien que vous pouvez spécifier une valeur explicite au lieu d’utiliser la valeur par défaut d’une colonne, il ne peut pas en être de même avec les colonnes calculées.

Pour remplacer la génération de valeurs par une valeur explicite, définissez simplement la propriété sur n’importe quelle valeur qui n’est pas la valeur par défaut CLR pour le type de cette propriété (null pour string, 0Guid.Empty pour int, etcGuid.).

Remarque

La tentative d’insertion de valeurs explicites dans SQL Server IDENTITY échoue par défaut ; Consultez ces documents pour obtenir une solution de contournement.

Pour fournir une valeur explicite pour les propriétés qui ont été configurées comme valeur générée lors de l’ajout ou de la mise à jour, vous devez également configurer la propriété comme suit :

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

Aucune génération de valeur

En dehors des scénarios spécifiques tels que ceux décrits ci-dessus, les propriétés n’ont généralement aucune génération de valeur configurée ; Cela signifie qu’il appartient à l’application de toujours fournir une valeur à enregistrer dans la base de données. Cette valeur doit être affectée aux nouvelles entités avant qu’elles soient ajoutées au contexte.

Toutefois, dans certains cas, vous pouvez désactiver la génération de valeur qui a été configurée par convention. Par exemple, une clé primaire de type int est généralement configurée implicitement en tant que valeur générée lors de l’ajout (par exemple, colonne d’identité sur SQL Server). Vous pouvez le désactiver en procédant comme suit :

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

    public string Url { get; set; }
}