我在使用C#Entity Framework 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#Code
插入记录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()
"无法将显式值插入到GENERATED ALWAYS列中 表' DevDB.dbo.People'。将INSERT与列列表一起使用以排除 生成ALWAYS列,或将DEFAULT插入GENERATED ALWAYS 。柱"
我尝试使用以下插入查询使用SQL Server直接插入,插入正常。
INSERT INTO [dbo].[People]
([PeopleID]
,[Name]
,[LastName]
,[NickName])
VALUES
(2
,'John'
,'Math'
,'COOL')
请帮助我如何使用C#Entity Framework插入记录。
答案 0 :(得分:4)
轻松摘要:当EF尝试更新PERIOD
系统版本控制列中的值时,会出现问题,列属性值由SQL Server本身管理。
从MS Docs: Temporal tables开始,时态表作为一对当前表和历史表工作,如下所示:
表的系统版本控制实现为一对表,a 当前表和历史表。在每个表中, 以下两个额外的datetime2列用于定义 每一行的有效期:
期间开始列:系统会记录此列中行的开始时间,通常表示为SysStartTime列。
期末列:系统会记录此列中行的结束时间,通常以SysEndTime列表示。
同时StartTime
& EndTime
列是自动生成的,必须将其排除在任何插入或更新值的尝试之外。假设您使用的是EF 6:
StartTime
& EndTime
选项中的Identity
列属性为StoreGeneratedPattern
。这可以防止EF刷新任何UPDATE
事件的值。创建一个自定义命令树拦截器类,它实现System.Data.Entity.Infrastructure.Interception.IDbCommandTreeInterceptor
并指定set子句,这些子句应设置为ReadOnlyCollection<T>
(T是DbModificationClause
),不能被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
}
}
相关问题:
Entity Framework not working with temporal table
Getting DbContext from implementation of IDbCommandInterceptor
Hooking IDbInterceptor to EntityFramework DbContext only once
答案 1 :(得分:0)
最简单的解决方案可能是手动编辑.EDMX文件并删除StartTime和EndTime列的所有痕迹。