Définition de valeurs explicites pour les propriétés généréesSetting Explicit Values for Generated Properties

Une propriété générée est une propriété dont la valeur est générée (soit par la base de données soit par EF) lorsque l’entité est ajoutée ou mise à jour.A generated property is a property whose value is generated (either by EF or the database) when the entity is added and/or updated. Pour plus d’informations, consultez Propriétés générées.See Generated Properties for more information.

Il peut arriver que vous souhaitiez définir une valeur explicite pour une propriété générée, au lieu d’en générer une.There may be situations where you want to set an explicit value for a generated property, rather than having one generated.

Conseil

Vous pouvez afficher cet exemple sur GitHub.You can view this article's sample on GitHub.

Le modèleThe model

Le modèle utilisé dans cet article contient une seule entité Employee.The model used in this article contains a single Employee entity.

public class Employee
{
    public int EmployeeId { get; set; }
    public string Name { get; set; }
    public DateTime EmploymentStarted { get; set; }
    public int Salary { get; set; }
    public DateTime? LastPayRaise { get; set; }
}

Enregistrement d’une valeur explicite lors de l’ajoutSaving an explicit value during add

La propriété Employee.EmploymentStarted est configurée pour avoir des valeurs générées par la base de données pour les nouvelles entités (avec une valeur par défaut).The Employee.EmploymentStarted property is configured to have values generated by the database for new entities (using a default value).

modelBuilder.Entity<Employee>()
    .Property(b => b.EmploymentStarted)
    .HasDefaultValueSql("CONVERT(date, GETDATE())");

Le code suivant insère deux employés dans la base de données.The following code inserts two employees into the database.

  • Pour le premier, aucune valeur n’est attribuée à la propriété Employee.EmploymentStarted, elle reste donc définie sur la valeur par défaut de CLR pour DateTime.For the first, no value is assigned to Employee.EmploymentStarted property, so it remains set to the CLR default value for DateTime.
  • Pour le deuxième, nous avons défini une valeur explicite de 1-Jan-2000.For the second, we have set an explicit value of 1-Jan-2000.
using (var context = new EmployeeContext())
{
    context.Employees.Add(new Employee { Name = "John Doe" });
    context.Employees.Add(new Employee { Name = "Jane Doe", EmploymentStarted = new DateTime(2000, 1, 1) });
    context.SaveChanges();

    foreach (var employee in context.Employees)
    {
        Console.WriteLine(employee.EmployeeId + ": " + employee.Name + ", " + employee.EmploymentStarted);
    }
}

La sortie indique que la base de données a généré une valeur pour le premier employé et utilisé notre valeur explicite pour le deuxième.Output shows that the database generated a value for the first employee and our explicit value was used for the second.

1: John Doe, 1/26/2017 12:00:00 AM
2: Jane Doe, 1/1/2000 12:00:00 AM

Valeurs explicites dans les colonnes IDENTITY de SQL ServerExplicit values into SQL Server IDENTITY columns

Par convention la propriété Employee.EmployeeId est une colonne IDENTITY générée par le magasin.By convention the Employee.EmployeeId property is a store generated IDENTITY column.

Pour la plupart des cas, l’approche illustrée ci-dessus fonctionne pour les propriétés de clé.For most situations, the approach shown above will work for key properties. Toutefois, pour insérer des valeurs explicites dans une colonne IDENTITY SQL Server, vous devez activer manuellement IDENTITY_INSERT avant d’appeler SaveChanges().However, to insert explicit values into a SQL Server IDENTITY column, you need to manually enable IDENTITY_INSERT before calling SaveChanges().

Note

Nous avons une demande de fonctionnalité dans notre backlog pour faire cela automatiquement dans le fournisseur SQL Server.We have a feature request on our backlog to do this automatically within the SQL Server provider.

using (var context = new EmployeeContext())
{
    context.Employees.Add(new Employee { EmployeeId = 100, Name = "John Doe" });
    context.Employees.Add(new Employee { EmployeeId = 101, Name = "Jane Doe" });

    context.Database.OpenConnection();
    try
    {
        context.Database.ExecuteSqlCommand("SET IDENTITY_INSERT dbo.Employees ON");
        context.SaveChanges();
        context.Database.ExecuteSqlCommand("SET IDENTITY_INSERT dbo.Employees OFF");
    }
    finally
    {
        context.Database.CloseConnection();
    }


    foreach (var employee in context.Employees)
    {
        Console.WriteLine(employee.EmployeeId + ": " + employee.Name);
    }
}

La sortie indique que les ID fournis ont été enregistrés dans la base de données.Output shows that the supplied ids were saved to the database.

100: John Doe
101: Jane Doe

Définition d’une valeur explicite pendant une mise à jourSetting an explicit value during update

La propriété Employee.LastPayRaise est configurée pour avoir des valeurs générées par la base de données lors des mises à jour.The Employee.LastPayRaise property is configured to have values generated by the database during updates.

modelBuilder.Entity<Employee>()
    .Property(b => b.LastPayRaise)
    .ValueGeneratedOnAddOrUpdate();

modelBuilder.Entity<Employee>()
    .Property(b => b.LastPayRaise)
    .Metadata.AfterSaveBehavior = PropertySaveBehavior.Ignore;

Note

Par défaut, EF Core lève une exception si vous essayez d’enregistrer une valeur explicite pour une propriété qui est configurée pour être générée pendant la mise à jour.By default, EF Core will throw an exception if you try to save an explicit value for a property that is configured to be generated during update. Pour éviter ce problème, vous devez descendre au niveau de l’API de bas niveau et définir AfterSaveBehavior (comme indiqué ci-dessus).To avoid this, you need to drop down to the lower level metadata API and set the AfterSaveBehavior (as shown above).

Note

Modifications dans EF Core 2.0 : dans les versions précédentes, le comportement après enregistrement était contrôlé par le biais de l’indicateur IsReadOnlyAfterSave.Changes in EF Core 2.0: In previous releases the after-save behavior was controlled through the IsReadOnlyAfterSave flag. Cet indicateur est obsolète et a été remplacé par AfterSaveBehavior.This flag has been obsoleted and replaced by AfterSaveBehavior.

Il existe également un déclencheur dans la base de données pour générer des valeurs pour la colonne LastPayRaise pendant les opérations UPDATE.There is also a trigger in the database to generate values for the LastPayRaise column during UPDATE operations.

CREATE TRIGGER [dbo].[Employees_UPDATE] ON [dbo].[Employees]
    AFTER UPDATE
AS
BEGIN
    SET NOCOUNT ON;
                  
    IF ((SELECT TRIGGER_NESTLEVEL()) > 1) RETURN;
                  
    IF UPDATE(Salary) AND NOT Update(LastPayRaise)
    BEGIN
        DECLARE @Id INT
        DECLARE @OldSalary INT
        DECLARE @NewSalary INT
          
        SELECT @Id = INSERTED.EmployeeId, @NewSalary = Salary        
        FROM INSERTED
          
        SELECT @OldSalary = Salary        
        FROM deleted
          
        IF @NewSalary > @OldSalary
        BEGIN
            UPDATE dbo.Employees
            SET LastPayRaise = CONVERT(date, GETDATE())
            WHERE EmployeeId = @Id
        END
    END
END

Le code suivant augmente le salaire des deux employés dans la base de données.The following code increases the salary of two employees in the database.

  • Pour le premier, aucune valeur n’est attribuée à la propriété Employee.LastPayRaise, elle reste donc définie sur null.For the first, no value is assigned to Employee.LastPayRaise property, so it remains set to null.
  • Pour le second, nous avons défini une valeur explicite d’une semaine plus tôt (rétroactivation de l’augmentation de salaire).For the second, we have set an explicit value of one week ago (back dating the pay raise).
using (var context = new EmployeeContext())
{
    var john = context.Employees.Single(e => e.Name == "John Doe");
    john.Salary = 200;

    var jane = context.Employees.Single(e => e.Name == "Jane Doe");
    jane.Salary = 200;
    jane.LastPayRaise = DateTime.Today.AddDays(-7);

    context.SaveChanges();

    foreach (var employee in context.Employees)
    {
        Console.WriteLine(employee.EmployeeId + ": " + employee.Name + ", " + employee.LastPayRaise);
    }
}

La sortie indique que la base de données a généré une valeur pour le premier employé et utilisé notre valeur explicite pour le deuxième.Output shows that the database generated a value for the first employee and our explicit value was used for the second.

1: John Doe, 1/26/2017 12:00:00 AM
2: Jane Doe, 1/19/2017 12:00:00 AM