5

Ich benutze Datenbank erste Entität Framework 6. Nachdem einige der Tabellen in meinem Schema, um temporale Tabellen zu ändern, habe ich den folgenden Fehler beim Versuch, neue Daten einzufügen:Entity Framework funktioniert nicht mit Temporal-Tabelle

Es sieht aus wie EF versucht, die Werte der PERIOD Spalten zu aktualisieren, die von dem System verwaltet werden.

Das Entfernen der Spalten aus der EDMX-Datei scheint das Problem zu beheben, aber dies ist keine praktikable Lösung, da die Spalten jedes Mal neu hinzugefügt werden, wenn das Modell aus der Datenbank neu generiert wird.

Antwort

7

Es gibt zwei Lösungen für dieses Problem:

  1. Im Eigenschaftsfenster für die Spalte in den EDMX Designern, die StoreGeneratedPattern auf den PERIOD Spalten ändern (ValidFrom und ValidTo in meinem Fall) sein, entweder identity oder computed. Die Identität ist wahrscheinlich besser, da die Berechnung dazu führt, dass EF die Werte bei einem Einfügen und Aktualisieren im Gegensatz zu nur einem Einfügen mit identity
  2. IDbCommandTreeInterceptor Implementierung aktualisiert, um die Periodenspalten zu entfernen. Dies ist meine bevorzugte Lösung, da beim Hinzufügen neuer Tabellen zum Modell keine zusätzliche Arbeit erforderlich ist.

Hier ist meine Implementierung:

using System.Data.Entity.Infrastructure.Interception; 
using System.Data.Entity.Core.Common.CommandTrees; 
using System.Data.Entity.Core.Metadata.Edm; 
using System.Collections.ObjectModel; 

internal class TemporalTableCommandTreeInterceptor : IDbCommandTreeInterceptor 
{ 
    private static readonly List<string> _namesToIgnore = new List<string> { "ValidFrom", "ValidTo" }; 

    public void TreeCreated(DbCommandTreeInterceptionContext interceptionContext) 
    { 
     if (interceptionContext.OriginalResult.DataSpace == DataSpace.SSpace) 
     { 
      var insertCommand = interceptionContext.Result as DbInsertCommandTree; 
      if (insertCommand != null) 
      { 
       var newSetClauses = GenerateSetClauses(insertCommand.SetClauses); 

       var newCommand = new DbInsertCommandTree(
        insertCommand.MetadataWorkspace, 
        insertCommand.DataSpace, 
        insertCommand.Target, 
        newSetClauses, 
        insertCommand.Returning); 

       interceptionContext.Result = newCommand; 
      } 

      var updateCommand = interceptionContext.Result as DbUpdateCommandTree; 
      if (updateCommand != null) 
      { 
       var newSetClauses = GenerateSetClauses(updateCommand.SetClauses); 

       var newCommand = new DbUpdateCommandTree(
        updateCommand.MetadataWorkspace, 
        updateCommand.DataSpace, 
        updateCommand.Target, 
        updateCommand.Predicate, 
        newSetClauses, 
        updateCommand.Returning); 

       interceptionContext.Result = newCommand; 
      } 
     } 
    } 

    private static ReadOnlyCollection<DbModificationClause> GenerateSetClauses(IList<DbModificationClause> modificationClauses) 
    { 
     var props = new List<DbModificationClause>(modificationClauses); 
     props = props.Where(_ => !_namesToIgnore.Contains((((_ as DbSetClause)?.Property as DbPropertyExpression)?.Property as EdmProperty)?.Name)).ToList(); 

     var newSetClauses = new ReadOnlyCollection<DbModificationClause>(props); 
     return newSetClauses; 
    } 
} 

Register diese Abfangjäger mit EF durch die folgenden überall in Ihrem Code ausgeführt wird, bevor Sie Ihren Kontext verwenden:

DbInterception.Add(new TemporalTableCommandTreeInterceptor()); 
+0

Wie kann ich das Gleiche mit Entity Framework Core tun? –

+0

@AramGevorgyan - Sie können das Attribut [DatabaseGenerated (DatabaseGeneratedOption.Computed)] für die Eigenschaften verwenden oder die Fluent-API-Methode .ValueGeneratedOnAddOrUpdate() z. entity.Property (e => e.ValidFrom) .ValueGeneratedOnAddOrUpdate(); [Siehe hier] (http://www.learnentityframeworkcore.com/configuration/data-annotation-attributes/databasegenerated-attribute) als Referenz. –

+1

Arbeitete wie ein Charme! Die "usings" sind wie folgt: using System.Data.Entity.Infrastructure.Interception; mit System.Data.Entity.Core.Common.CommandTrees; mit System.Data.Entity.Core.Metadata.Edm; mit System.Collections.ObjectModel; ' – mike123

0

Eine andere Lösung ist Standardeinschränkung erstellen in den Feldern der Tabelle.

CREATE TABLE [dbo].[Table] (
    [Id]   INT IDENTITY(1, 1) NOT NULL, 
    [Description] NVARCHAR(100)  NOT NULL, 
    [ValidFrom]  DATETIME2(0)  GENERATED ALWAYS AS ROW START HIDDEN CONSTRAINT [Df_Table_ValidFrom] DEFAULT DATEADD(SECOND, -1, SYSUTCDATETIME()), 
    [ValidTo]  DATETIME2(0)  GENERATED ALWAYS AS ROW END HIDDEN CONSTRAINT [Df_Table_ValidTo] DEFAULT '9999.12.31 23:59:59.99', 
    PERIOD FOR SYSTEM_TIME ([ValidFrom], [ValidTo]), 
    CONSTRAINT [Pk_Table] PRIMARY KEY CLUSTERED ([Id] ASC) 
) WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = [dbo].[Table_History])); 
GO 

In dem Code muss keine Änderung nichts.

Verwandte Themen