[英]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 列中表示。
由於StartTime
和EndTime
列都是自動生成的,因此必須將它們排除在任何嘗試插入或更新它們的值之外。 假設您使用的是 EF 6,以下是消除錯誤的步驟:
StoreGeneratedPattern
選項StoreGeneratedPattern
StartTime
和EndTime
列屬性設置為Identity
。 這可以防止 EF 在任何UPDATE
事件上刷新值。 創建一個自定義命令樹攔截器類,該類實現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; } }
仍然在上面的同一個類中,創建被忽略的表名列表並在 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; } } }
通過使用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 } }
相關問題:
可能最簡單的解決方案是手動編輯 .EDMX 文件並刪除 StartTime 和 EndTime 列的所有痕跡。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.