簡體   English   中英

使用 C# 實體框架在臨時表中插入記錄

[英]Insert Record in Temporal Table using C# Entity Framework

我在使用 C# 實體框架在Temporal table中插入數據時遇到問題

表模式是

CREATE TABLE People( 
    PeopleID int PRIMARY KEY NOT NULL, 
    Name varchar(50) Null, 
    LastName varchar(100) NULL, 
    NickName varchar(25), 
    StartTime datetime2 GENERATED ALWAYS AS ROW START NOT NULL, 
    EndTime datetime2 GENERATED ALWAYS AS ROW END NOT NULL, 
    PERIOD FOR SYSTEM_TIME (StartTime,EndTime) 
) WITH (SYSTEM_VERSIONING = ON(HISTORY_TABLE = dbo.PeopleHistory));

我創建了一個 EDMX asusal 並嘗試使用以下 C# 代碼插入記錄

using (var db = new DevDBEntities()) {
    People1 peo = new People1() {
        PeopleID = 1,
        Name = "Emma",
        LastName = "Watson",
        NickName = "ICE"
    };

    db.Peoples.Add(peo);
    db.SaveChanges();
}

我在db.SaveChanges()db.SaveChanges()異常

“無法將顯式值插入到表 'DevDB.dbo.People' 中的 GENERATED ALWAYS 列中。將 INSERT 與列列表一起使用以排除 GENERATED ALWAYS 列,或將 DEFAULT 插入到 GENERATED ALWAYS 列中。”

我嘗試使用 SQL Server 使用以下插入查詢直接插入,它的插入很好。

INSERT INTO [dbo].[People]
           ([PeopleID]
           ,[Name]
           ,[LastName]
           ,[NickName])
     VALUES
           (2
           ,'John'
           ,'Math'
           ,'COOL')

請幫助我如何使用 C# 實體框架插入記錄。

簡單摘要:當 EF 嘗試更新列屬性值由 SQL Server 本身管理的PERIOD系統版本控制列內的值時,會出現問題。

MS Docs: Temporal tables ,臨時表作為一對當前表和歷史表工作,解釋如下:

表的系統版本控制被實現為一對表、一個當前表和一個歷史表。 在這些表中的每一個中,以下兩個額外的 datetime2 列用於定義每行的有效期:

Period start 列:系統記錄此列中行的開始時間,通常表示為 SysStartTime 列。

期間結束列:系統在此列中記錄行的結束時間,通常在 SysEndTime 列中表示。

由於StartTimeEndTime列都是自動生成的,因此必須將它們排除在任何嘗試插入或更新它們的值之外。 假設您使用的是 EF 6,以下是消除錯誤的步驟:

  1. 在設計器模式下打開 EDMX 文件,在StoreGeneratedPattern選項StoreGeneratedPattern StartTimeEndTime列屬性設置為Identity 這可以防止 EF 在任何UPDATE事件上刷新值。

標識列設置

  1. 創建一個自定義命令樹攔截器類,該類實現System.Data.Entity.Infrastructure.Interception.IDbCommandTreeInterceptor並指定應設置為ReadOnlyCollection<T> (T 是DbModificationClause )的 set 子句,EF 在插入或更新修改中不能對其進行修改:

     internal class TemporalTableCommandTreeInterceptor : IDbCommandTreeInterceptor { private static ReadOnlyCollection<DbModificationClause> GenerateSetClauses(IList<DbModificationClause> modificationClauses) { var props = new List<DbModificationClause>(modificationClauses); props = props.Where(_ => !_ignoredColumns.Contains((((_ as DbSetClause)?.Property as DbPropertyExpression)?.Property as EdmProperty)?.Name)).ToList(); var newSetClauses = new ReadOnlyCollection<DbModificationClause>(props); return newSetClauses; } }
  2. 仍然在上面的同一個類中,創建被忽略的表名列表並在 INSERT 和 UPDATE 命令中定義操作,該方法應該如下所示(此方法歸功於 Matt Ruwe):

     // from /a/40742144 private static readonly List<string> _ignoredColumns = new List<string> { "StartTime", "EndTime" }; 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; } } }
  3. 通過使用DbInterception在另一個代碼部分中的數據庫上下文使用之前注冊上面的攔截器類:

     DbInterception.Add(new TemporalTableCommandTreeInterceptor());

    或使用DbConfigurationTypeAttribute將其附加到上下文定義中:

     public class CustomDbConfiguration : DbConfiguration { public CustomDbConfiguration() { this.AddInterceptor(new TemporalTableCommandTreeInterceptor()); } } // from /a/40302086 [DbConfigurationType(typeof(CustomDbConfiguration))] public partial class DataContext : System.Data.Entity.DbContext { public DataContext(string nameOrConnectionString) : base(nameOrConnectionString) { // other stuff or leave this blank } }

相關問題:

實體框架不適用於時態表

從 IDbCommandInterceptor 的實現中獲取 DbContext

僅將 IDbInterceptor 與 EntityFramework DbContext 掛鈎一次

可能最簡單的解決方案是手動編輯 .EDMX 文件並刪除 StartTime 和 EndTime 列的所有痕跡。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM