5

工作我使用的數據庫第一實體框架6.改變某些表在我的架構後進行時態表,我開始試圖插入新的數據時,出現以下錯誤:實體框架不與時態表

Cannot insert an explicit value into a GENERATED ALWAYS column in table '<MyDatabase>.dbo.<MyTableName>. Use INSERT with a column list to exclude the GENERATED ALWAYS column, or insert a DEFAULT into GENERATED ALWAYS column.

看起來EF正試圖更新由系統管理的PERIOD列的值。

從EDMX文件中刪除列似乎可以解決問題,但這不是一個可行的解決方案,因爲每次從數據庫重新生成模型時都會重新添加列。

回答

7

有兩個解決這個問題:

  1. 在在EDMX設計師的列的屬性窗口,更改的PERIODStoreGeneratedPattern(ValidFrom和ValidTo在我的情況中)爲identitycomputed。標識可能會更好,因爲計算結果會導致EF刷新插入和更新上的值,而不僅僅是插入identity
  2. 創建IDbCommandTreeInterceptor實現以刪除週期列。這是我首選的解決方案,因爲向模型添加新表格時不需要額外的工作。

這裏是我的實現:

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; 
    } 
} 

註冊這個攔截器與EF運行你用你的背景前,在你的代碼的任何地方如下:

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

我如何用實體框架核心做同樣的事情? –

+0

@AramGevorgyan - 您可以在屬性上使用屬性[DatabaseGenerated(DatabaseGeneratedOption.Computed)],或者使用Fluent API方法.ValueGeneratedOnAddOrUpdate() entity.Property(e => e.ValidFrom).ValueGeneratedOnAddOrUpdate(); [見這裏](http://www.learnentityframeworkcore.com/configuration/data-annotation-attributes/databasegenerated-attribute)以供參考。 –

+1

工作就像一個魅力! 'usings'如下'使用System.Data.Entity.Infrastructure.Interception; using System.Data.Entity.Core.Common.CommandTrees; using System.Data.Entity.Core.Metadata.Edm; using System.Collections.ObjectModel;' – mike123

0

另一個解決方法是創建默認約束在桌子的領域。

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 

在代碼中不需要更改什麼。