entity framework不使用时态表
我正在使用数据库第一entity framework6.在将我的模式中的一些表更改为临时表后,我在尝试插入新数据时开始收到以下错误:
Cannot insert an explicit value into a GENERATED ALWAYS column in table '.dbo.. Use INSERT with a column list to exclude the GENERATED ALWAYS column, or insert a DEFAULT into GENERATED ALWAYS column.
看起来EF正在尝试更新由系统管理的PERIOD
列的值。
从EDMX文件中删除列似乎可以解决问题,但这不是一个可行的解决方案,因为每次从数据库重新生成模型时都会重新添加列。
这个问题有两种解决方案:
- 在EDMX设计器中列的属性窗口中,将
PERIOD
列(我的情况下为ValidFrom和ValidTo)上的StoreGeneratedPattern
更改为identity
或computed
。 标识可能更好,因为计算将导致EF刷新插入和更新上的值,而不是仅具有identity
的插入 - 创建一个
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 _namesToIgnore = new List { "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 GenerateSetClauses(IList modificationClauses) { var props = new List (modificationClauses); props = props.Where(_ => !_namesToIgnore.Contains((((_ as DbSetClause)?.Property as DbPropertyExpression)?.Property as EdmProperty)?.Name)).ToList(); var newSetClauses = new ReadOnlyCollection (props); return newSetClauses; } }
在使用上下文之前,通过在代码中的任何位置运行以下命令来向EF注册此拦截器:
DbInterception.Add(new TemporalTableCommandTreeInterceptor());
另一种解决方案是在表的字段中创建默认约束。
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
在代码中不需要改动。