在 Entity Framework Core 中复制整行
Posted
技术标签:
【中文标题】在 Entity Framework Core 中复制整行【英文标题】:Replicate Entire Row In Entity Framework Core 【发布时间】:2018-08-28 22:16:04 【问题描述】:我正在尝试从数据库中检索一行,更改其中的某些列值并将其添加为新行(实体框架核心),
但它给了我错误
当 IDENTITY_INSERT 设置为 OFF 时,无法为表“Audit_Schedules”中的标识列插入显式值。
这个表有一个主键"ScheduleId"
下面是我的代码
AuditSchedules _schedules = new AuditSchedules();
using (var ctx = new QuestionnaireEntities(_configuration))
_schedules = ctx.AuditSchedules.Where(x => x.ScheduleId == model.ScheduleID).SingleOrDefault();
_schedules.StaffId = model.TransferedAuditorCode;
_schedules.StaffName = model.TransferedAuditorName;
_schedules.FromDate = _schedules.ToDate = Convert.ToDateTime(model.TransferedScheduleDate);
ctx.AuditSchedules.Add(_schedules);
ctx.SaveChanges();
_subschedules = ctx.AuditSubSchedule.Where(x => x.SubScheduleId == model.SubScheduleID).SingleOrDefault();
_subschedules.IsHoliDay = "Y";
_subschedules.HolidayType = model.HolidayType;
_subschedules.TransferedScheduleId = _schedules.ScheduleId.ToString();
ctx.AuditSubSchedule.Update(_subschedules);
ctx.SaveChanges();
错误出现
ctx.AuditSchedules.Add(_schedules);
首先我认为它的 Schedule_ID 值冲突并且无法添加重复的 Primary Key ,但是 Schedule_ID 是自动生成的字段,因此不应该发生此问题
我也尝试将其设置为不同的值
_schedules.ScheduleId = 0;
但它不插入 . 如何复制其中几乎没有更改的行(想要添加新行但修改了值)
【问题讨论】:
创建一个新对象并复制更多字段要干净得多。此外,您的 SingleOrDefault() 应该只是 Single() 。 错误已经很清楚了,需要清除两个表的PK。 您没有告诉 EFScheduleId
是一个标识列,因此它会尝试向其中插入一个值。
@GertArnold ,比我们使用 Add() 方法时 SQL 如何生成它的值,
您知道什么是身份列,对吗?任何插入都会生成下一个值。 Schedule_ID
不应该出现在 EF 生成的插入语句中,这才是重点。
【参考方案1】:
在插入时自动生成值的 EF Core 行为与 EF6 不同。
首先,该属性必须具有默认值 (0
) 才能自动生成。这允许身份插入,这在 EF6 中是不可能的。
其次,被添加的实体不应该被上下文跟踪,否则上下文会在内部保留一些实体键已设置的信息,并将在生成的INSERT
中包含值(甚至0
)命令,这反过来会导致你得到的异常。
为实现目标,在调用Add
方法之前:
首先确保在获取实体时不使用No-tracking query跟踪实体
_schedules = ctx.AuditSchedules
.AsNoTracking() // <--
.Where(x => x.ScheduleId == model.ScheduleID)
.SingleOrDefault();
或显式分离它
ctx.Entry(_schedules).State = EntityState.Detached;
然后重置PK
_schedules.ScheduleId = 0;
做其他修改,最后调用
ctx.AuditSchedules.Add(_schedules);
这适用于没有导航属性/FK 的简单实体。对于复杂的实体图,您应该不使用跟踪查询,然后以与分离实体图相同的方式使用它。
【讨论】:
AsNoTracking() 没有进入智能 那你需要using Microsoft.EntityFrameworkCore;
(很多EF Core方法都是通过扩展方法提供的)
我通过在 Id 属性上使用 UseSqlServerIdentityColumn()
,使用 AsNoTracking
并将添加对象的 id 值设置为 0 来实现此功能。当您提到该属性需要默认值时,请执行您的意思是它必须是具有非空默认值的(CLR)数据类型?在我的测试中,HasDefaultValue(0)
不是必需的(并且是多余的)。顺便说一句,很高兴知道 EF-core 与 EF6 不同的另一个领域。这在复制到其他数据库时可能非常有用。现在无需篡改映射即可完成。
嗨@Gert我假设该属性已经是标识列,否则OP不会得到那个异常。是的,我的意思是将对象的自动生成属性设置为 CLR default
值。与 EF6 的主要区别在于,如您所知,EF6 始终忽略标记为 Added
的实体的标识列的当前值,而 EF Core 尊重它。实际上,这允许在不更改映射的情况下插入身份:)【参考方案2】:
错误很简单,这是因为您将身份插入添加为插入的一部分。如果是身份,则必须自动生成。因此,要么在插入之前关闭,然后再将其打开。或者让它自动生成。
如果您尝试从 sql server 插入相同的数据,此错误将相同。 这基本上是从 sql server 传播过来的。
如果您不希望数据库生成 ID,则应在模型上使用 DatabaseGenerated 属性,如
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int ScheduleId get;set;
如果你想关闭身份插入,你可以试试here
【讨论】:
我不想关闭它,我想将它添加为表中具有新标识值的新记录 和 Add() 方法一样,我们不需要提供这个字段的值,SQL 自动生成它 不想设置就让sql处理吧。以上是关于在 Entity Framework Core 中复制整行的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Entity Framework Core 中运行存储过程?
如何在 Entity Framework Core 中运行存储过程?
什么是自有实体?何时以及为什么在 Entity Framework Core 中使用 Owned Entity?
Entity Framework Core 中 TimeSpan 的总和
Entity Framework优化一:引发了“System.Data.Entity.Core.EntityCommandExecutionException”类型的异常