Freigeben über


Generierte Werte

Datenbankspalten können ihre Werte auf verschiedene Arten generieren: Primärschlüsselspalten sind häufig automatisch inkrementierte Ganzzahlen, bei anderen Spalten handelt es sich um Standard- oder berechnete Werte usw. Auf dieser Seite werden verschiedene Muster für die Generierung von Konfigurationswerten mit EF Core beschrieben.

Standardwerte

In relationalen Datenbanken kann eine Spalte mit einem Standardwert konfiguriert werden. wenn eine Zeile ohne Wert für diese Spalte eingefügt wird, wird der Standardwert verwendet.

Sie können einen Standardwert für eine Eigenschaft konfigurieren:

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

Sie können auch ein SQL-Fragment angeben, das zum Berechnen des Standardwerts verwendet wird:

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

Ab EF 10 können Sie für SQL Server den Namen für Standardwertseinschränkungen explizit angeben, sodass Sie ihr Datenbankschema besser steuern können.

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

Sie können auch aufrufen UseNamedDefaultConstraints , um die automatische Benennung aller Standardeinschränkungen zu aktivieren. Beachten Sie, dass bei vorhandenen Migrationen die nächste hinzugefügte Migration jede einzelne Standardeinschränkung in Ihrem Modell umbenennen wird.

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

Berechnete Spalten

In den meisten relationalen Datenbanken kann eine Spalte so konfiguriert werden, dass ihr Wert in der Datenbank berechnet wird, in der Regel mit einem Ausdruck, der auf andere Spalten verweist:

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

Im obigen Beispiel wird eine virtuelle berechnete Spalte erstellt, deren Wert jedes Mal berechnet wird, wenn er aus der Datenbank abgerufen wird. Sie können auch angeben, dass eine berechnete Spalte gespeichert wird (manchmal als persistiert bezeichnet), was bedeutet, dass sie für jede Aktualisierung der Zeile berechnet wird und auf dem Datenträger zusammen mit regulären Spalten gespeichert wird:

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

Primärschlüssel

Standardmäßig werden nicht zusammengesetzte Primärschlüssel vom Typ "short", "int", "long" oder "GUID" so eingerichtet, dass Werte für eingefügte Entitäten generiert werden, wenn ein Wert nicht von der Anwendung bereitgestellt wird. Ihr Datenbankanbieter kümmert sich in der Regel um die erforderliche Konfiguration. Beispielsweise wird automatisch ein numerischer Primärschlüssel in SQL Server als IDENTITÄTsspalte eingerichtet.

Weitere Informationen finden Sie in der Dokumentation zu Schlüsseln und Anleitungen für bestimmte Vererbungszuordnungsstrategien.

Explizite Konfiguration der Wertgenerierung

Wir haben oben gesehen, dass EF Core automatisch die Wertgenerierung für Primärschlüssel einrichte - aber möglicherweise möchten wir dasselbe für Nichtschlüsseleigenschaften. Sie können jede Eigenschaft so konfigurieren, dass ihr Wert für eingefügte Entitäten wie folgt generiert wird:

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

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

Ebenso kann eine Eigenschaft so konfiguriert werden, dass ihr Wert beim Hinzufügen oder Aktualisieren generiert wird:

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

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

Im Gegensatz zu Standardwerten oder berechneten Spalten geben wir nicht an , wie die Werte generiert werden sollen. das hängt vom verwendeten Datenbankanbieter ab. Datenbankanbieter können die Wertgenerierung für einige Eigenschaftstypen automatisch einrichten, andere erfordern jedoch möglicherweise, dass Sie manuell einrichten, wie der Wert generiert wird.

Wenn zum Beispiel auf einem SQL Server eine GUID-Eigenschaft als Primärschlüssel konfiguriert ist, führt der Anbieter automatisch die Wertgenerierung clientseitig durch, wobei ein Algorithmus verwendet wird, um optimale sequenzielle GUID-Werte zu erzeugen. Die Angabe ValueGeneratedOnAdd einer DateTime-Eigenschaft hat jedoch keine Auswirkung (siehe abschnitt unten für die DateTime-Wertgenerierung).

Ebenso werden die Byte[]-Eigenschaften, die so konfiguriert sind, dass sie beim Hinzufügen oder Aktualisieren automatisch generiert werden und als Parallelitätstoken gekennzeichnet sind, mit dem Datentyp "rowversion" eingerichtet, sodass Werte automatisch in der Datenbank generiert werden. Die Angabe ValueGeneratedOnAdd hat jedoch keine Auswirkung.

In der Dokumentation Ihres Anbieters finden Sie die unterstützten Spezifischen Techniken zur Wertgenerierung. Die Dokumentation zur SQL Server-Wertgenerierung finden Sie hier.

Generierung von Datums-/Uhrzeitwerten

Eine häufige Anforderung besteht darin, eine Datenbankspalte zu haben, die das Datum/die Uhrzeit enthält, zu dem die Zeile zum ersten Mal eingefügt wurde (Wert, der beim Hinzufügen generiert wurde) oder wann sie zuletzt aktualisiert wurde (Wert, der beim Hinzufügen oder Aktualisieren generiert wurde). Da es hierfür verschiedene Strategien gibt, richten EF Core-Anbieter in der Regel keine Wertgenerierung automatisch für Datums-/Uhrzeitspalten ein – Sie müssen dies selbst konfigurieren.

Erstellungszeitstempel

Das Konfigurieren einer Datums-/Uhrzeitspalte für den Zeitstempel der Zeile ist in der Regel eine Frage der Konfiguration eines Standardwerts mit der entsprechenden SQL-Funktion. In SQL Server können Sie beispielsweise Folgendes verwenden:

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

Achten Sie darauf, die entsprechende Funktion auszuwählen, da mehrere vorhanden sein können (z. B. GETDATE() vs. GETUTCDATE()).

Zeitstempel aktualisieren

Obwohl gespeicherte berechnete Spalten wie eine gute Lösung zum Verwalten von Zeitstempeln der letzten Aktualisierung erscheinen, erlauben Datenbanken in der Regel nicht, Funktionen wie GETDATE() in einer berechneten Spalte anzugeben. Alternativ können Sie einen Datenbanktrigger einrichten, um denselben Effekt zu erzielen:

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

Informationen zum Erstellen von Triggern finden Sie in der Dokumentation zur Verwendung von rohem SQL in Migrationen.

Überschreiben der Wertgenerierung

Obwohl eine Eigenschaft für die Wertgenerierung konfiguriert ist, können Sie in vielen Fällen dennoch explizit einen Wert dafür angeben. Ob dies tatsächlich funktioniert, hängt von dem spezifischen Mechanismus der Wertgenerierung ab, der konfiguriert wurde; Sie können zwar einen expliziten Wert angeben, anstatt den Standardwert einer Spalte zu verwenden, aber nicht mit berechneten Spalten.

Wenn Sie die Wertgenerierung mit einem expliziten Wert überschreiben möchten, legen Sie einfach die Eigenschaft auf einen beliebigen Wert fest, der nicht der CLR-Standardwert für den Typ dieser Eigenschaft ist (null für string, 0 für , int für Guid.Empty, uswGuid.).

Hinweis

Der Versuch, explizite Werte in SQL Server IDENTITY einzufügen, schlägt standardmäßig fehl; eine Problemumgehung finden Sie in diesen Dokumenten.

Um einen expliziten Wert für Eigenschaften bereitzustellen, die beim Hinzufügen oder Aktualisieren als Wert konfiguriert wurden, müssen Sie die Eigenschaft auch wie folgt konfigurieren:

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

Keine Wertgenerierung

Neben bestimmten Szenarien wie den oben beschriebenen Szenarien sind Eigenschaften in der Regel nicht für die Wertgenerierung konfiguriert. Dies bedeutet, dass die Anwendung immer einen Wert angibt, der in der Datenbank gespeichert werden soll. Dieser Wert muss neuen Entitäten zugewiesen werden, bevor sie dem Kontext hinzugefügt werden.

In einigen Fällen möchten Sie jedoch möglicherweise die Wertgenerierung deaktivieren, die nach Konventionen eingerichtet wurde. Beispielsweise wird ein Primärschlüssel vom Typ "int" in der Regel implizit als wertgeneriert-beim-Hinzufügen konfiguriert (z. B. als Identitätsspalte auf dem SQL Server). Sie können dies über Folgendes deaktivieren:

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

    public string Url { get; set; }
}