Dela via


Genererade värden

Databaskolumner kan få sina värden genererade på olika sätt: primära nyckelkolumner är ofta automatiskt inkrementella heltal, andra kolumner har standardvärden eller beräknade värden osv. På den här sidan beskrivs olika mönster för generering av konfigurationsvärde med EF Core.

Standardvärden

På relationsdatabaser kan en kolumn konfigureras med ett standardvärde. Om en rad infogas utan ett värde för den kolumnen används standardvärdet.

Du kan konfigurera ett standardvärde för en egenskap:

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

Du kan också ange ett SQL-fragment som används för att beräkna standardvärdet:

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

Från och med EF 10 kan du för SQL Server uttryckligen ange namnet på standardvärdebegränsningar, vilket ger dig mer kontroll över databasschemat.

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

Du kan också anropa UseNamedDefaultConstraints för att aktivera automatisk namngivning av alla standardbegränsningar. Observera att om du har befintliga migreringar kommer nästa migrering som du lägger till att byta namn på varje standardvillkor i din modell.

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

Beräknade kolumner

På de flesta relationsdatabaser kan en kolumn konfigureras så att dess värde beräknas i databasen, vanligtvis med ett uttryck som refererar till andra kolumner:

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

Ovanstående skapar en virtuell beräknad kolumn, vars värde beräknas varje gång den hämtas från databasen. Du kan också ange att en beräknad kolumn ska lagras (kallas ibland sparad), vilket innebär att den beräknas vid varje uppdatering av raden och lagras på disken tillsammans med vanliga kolumner:

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

Primärnycklar

Enligt konventionen konfigureras icke-sammansatta primära nycklar av typen kort, int, lång eller Guid så att värden genereras för infogade entiteter om ett värde inte tillhandahålls av programmet. Databasleverantören tar vanligtvis hand om den nödvändiga konfigurationen. En numerisk primärnyckel i SQL Server konfigureras till exempel automatiskt som en identitetskolumn.

Mer information finns i dokumentationen om nycklar och vägledning för specifika strategier för arvsmappning.

Explicit konfigurering av värdegenerering

Vi såg ovan att EF Core automatiskt konfigurerar värdegenerering för primära nycklar , men vi kanske vill göra samma sak för icke-nyckelegenskaper. Du kan konfigurera vilken egenskap som helst så att dess värde genereras för infogade entiteter på följande sätt:

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

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

På samma sätt kan en egenskap konfigureras så att dess värde genereras vid tillägg eller uppdatering:

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

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

Till skillnad från standardvärden eller beräknade kolumner anger vi inte hur värdena ska genereras. som är beroende av vilken databasprovider som används. Databasprovidrar kan automatiskt konfigurera värdegenerering för vissa egenskapstyper, men andra kan kräva att du konfigurerar hur värdet genereras manuellt.

När till exempel en GUID-egenskap konfigureras som en primärnyckel på SQL Server, utför providern automatiskt värdegenereringsklientsidan med hjälp av en algoritm för att generera optimala sekventiella GUID-värden. Att ange ValueGeneratedOnAdd på en DateTime-egenskap har dock ingen effekt (se avsnittet nedan för DateTime-värdegenerering).

På samma sätt är egenskaper av typen byte[] som konfigurerats att genereras vid tillägg eller uppdatering och markerade som samtidighetsmarkörer inställda med datatypen rowversion, så att värden genereras automatiskt i databasen. Att ange ValueGeneratedOnAdd har dock ingen effekt.

Se leverantörens dokumentation för de specifika värdegenereringstekniker som den stöder. Dokumentationen för SQL Server-värdegenerering finns här.

Generering av datum/tid-värde

En vanlig begäran är att ha en databaskolumn som innehåller datum/tid för när raden först infogades (värde som genererades vid tillägg) eller för när den senast uppdaterades (värde som genererades vid tillägg eller uppdatering). Eftersom det finns olika strategier för att göra detta konfigurerar EF Core-leverantörer vanligtvis inte värdegenerering automatiskt för datum/tid-kolumner – du måste konfigurera detta själv.

Tidsstämpel för skapande

Att konfigurera en datum/tid-kolumn för att skapa tidsstämpeln för raden handlar vanligtvis om att konfigurera ett standardvärde med lämplig SQL-funktion. På SQL Server kan du till exempel använda följande:

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

Se till att välja lämplig funktion, eftersom flera kan finnas (t.ex. GETDATE() jämfört med GETUTCDATE()).

Uppdatera tidsstämpel

Även om lagrade beräknade kolumner verkar vara en bra lösning för att hantera senast uppdaterade tidsstämplar, tillåter databaser vanligtvis inte att ange funktioner som GETDATE() i en beräknad kolumn. Alternativt kan du konfigurera en databasutlösare för att uppnå samma effekt:

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

Information om hur du skapar utlösare finns i dokumentationen om hur du använder rå SQL i migreringar.

Åsidosättande värdegenerering

Även om en egenskap har konfigurerats för värdegenerering kan du i många fall fortfarande uttryckligen ange ett värde för den. Om detta verkligen fungerar beror på den specifika värdegenereringsmekanism som har konfigurerats. även om du kan ange ett explicit värde i stället för att använda en kolumns standardvärde, kan samma sak inte göras med beräknade kolumner.

Om du vill åsidosätta värdegenerering med ett explicit värde anger du helt enkelt egenskapen till alla värden som inte är CLR-standardvärdet för den egenskapens typ (null för string, 0 för int, Guid.Empty för Guidosv.).

Anmärkning

Det går inte att infoga explicita värden i SQL Server IDENTITY som standard. Se dessa dokument för en lösning.

Om du vill ange ett explicit värde för egenskaper som har konfigurerats som värde som genererats vid tillägg eller uppdatering måste du också konfigurera egenskapen på följande sätt:

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

Ingen värdegenerering

Förutom specifika scenarier, till exempel de som beskrivs ovan, har egenskaperna vanligtvis ingen konfigurerad värdegenerering. Det innebär att det är upp till programmet att alltid ange ett värde som ska sparas i databasen. Det här värdet måste tilldelas till nya entiteter innan de läggs till i kontexten.

Men i vissa fall kanske du vill inaktivera värdegenerering som har konfigurerats enligt konvention. Till exempel är en primärnyckel av typen int vanligtvis implicit konfigurerad som värdegenererad vid tillägg (t.ex. identitetskolumn på SQL Server). Du kan inaktivera detta via följande:

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

    public string Url { get; set; }
}