Festlegen expliziter Werte für generierte EigenschaftenSetting Explicit Values for Generated Properties

Eine generierte Eigenschaft ist eine Eigenschaft, deren Wert (über EF oder die Datenbank) generiert wird, wenn die Entität hinzugefügt oder aktualisiert wird.A generated property is a property whose value is generated (either by EF or the database) when the entity is added and/or updated. Weitere Informationen finden Sie unter Generated Properties (Generierte Eigenschaften).See Generated Properties for more information.

Es gibt möglicherweise Situationen, in denen Sie einen expliziten Wert für einen generierten Wert festlegen möchten, anstatt einen zu generieren.There may be situations where you want to set an explicit value for a generated property, rather than having one generated.

Tipp

Das in diesem Artikel verwendete Beispiel finden Sie auf GitHub.You can view this article's sample on GitHub.

Das ModellThe model

Das in diesem Artikel verwendete Modell enthält eine einzelne Employee-Entität.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; }
}

Speichern eines expliziten Werts während des HinzufügensSaving an explicit value during add

Die Eigenschaft Employee.EmploymentStarted ist so konfiguriert, dass ihre Werte von der Datenbank für neue Entitäten generiert werden (mithilfe eines Standardwerts).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())");

Der folgende Code fügt zwei Mitarbeiter in die Datenbank ein.The following code inserts two employees into the database.

  • Für den ersten wird der Eigenschaft Employee.EmploymentStarted kein Wert zugewiesen, sodass sie weiterhin auf den CLR-Standardwert für DateTime festgelegt ist.For the first, no value is assigned to Employee.EmploymentStarted property, so it remains set to the CLR default value for DateTime.
  • Für den zweiten wird der explizite Wert 1-Jan-2000 festgelegt.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);
    }
}

Die Ausgabe zeigt, dass die Datenbank einen Wert für den ersten Mitarbeiter generiert hat und der explizite Wert für den zweiten Mitarbeiter verwendet wurde.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

Einfügen von expliziten Werten in IDENTITY-Spalten von SQL ServerExplicit values into SQL Server IDENTITY columns

Gemäß der Konvention ist die Eigenschaft Employee.EmployeeId eine im Speicher generierte IDENTITY-Spalte.By convention the Employee.EmployeeId property is a store generated IDENTITY column.

In den meisten Fällen funktioniert der oben gezeigte Ansatz für Schlüsseleigenschaften.For most situations, the approach shown above will work for key properties. Zum Einfügen expliziter Werte in eine IDENTITY-Spalte von SQL Server müssen Sie jedoch IDENTITY_INSERT manuell aktivieren, bevor Sie SaveChanges() aufrufen.However, to insert explicit values into a SQL Server IDENTITY column, you need to manually enable IDENTITY_INSERT before calling SaveChanges().

Hinweis

Die Automatisierung dieses Vorgangs im SQL Server-Anbieter ist eine Featureanforderung, die sich im Backlog befindet.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);
    }
}

Die Ausgabe zeigt an, dass die angegebenen IDs in der Datenbank gespeichert wurden.Output shows that the supplied ids were saved to the database.

100: John Doe
101: Jane Doe

Festlegen eines expliziten Werts während eines UpdatesSetting an explicit value during update

Die Eigenschaft Employee.LastPayRaise ist so konfiguriert, dass ihre Werte während Updates von der Datenbank generiert werden.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;

Hinweis

EF Core löst standardmäßig eine Ausnahme aus, wenn Sie versuchen einen expliziten Wert für eine Eigenschaft zu speichern, die so konfiguriert ist, dass er während des Updates generiert wird.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. Sie können dies umgehen, indem Sie wie oben gezeigt AfterSaveBehavior in der Metadaten-API festlegen.To avoid this, you need to drop down to the lower level metadata API and set the AfterSaveBehavior (as shown above).

Hinweis

Änderungen in EF Core 2.0: In vorherigen Releases wurde das Verhalten nach dem Speichern durch das Flag IsReadOnlyAfterSave gesteuert.Changes in EF Core 2.0: In previous releases the after-save behavior was controlled through the IsReadOnlyAfterSave flag. Das Flag ist veraltet und wurde durch AfterSaveBehavior ersetzt.This flag has been obsoleted and replaced by AfterSaveBehavior.

Außerdem gibt es in der Datenbank einen Trigger zum Generieren von Werten für die LastPayRaise-Spalte während UPDATE-Vorgängen.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

Im folgenden Code wird das Gehalt von zwei Mitarbeitern in der Datenbank erhöht.The following code increases the salary of two employees in the database.

  • Für den ersten wird der Eigenschaft Employee.LastPayRaise kein Wert zugewiesen, sodass sie weiterhin auf NULL festgelegt ist.For the first, no value is assigned to Employee.LastPayRaise property, so it remains set to null.
  • Für den zweiten wird ein expliziter Wert von vor einer Woche festgelegt (Rückdatierung der Gehaltserhöhung).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);
    }
}

Die Ausgabe zeigt, dass die Datenbank einen Wert für den ersten Mitarbeiter generiert hat und der explizite Wert für den zweiten Mitarbeiter verwendet wurde.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