在 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。 您没有告诉 EF ScheduleId 是一个标识列,因此它会尝试向其中插入一个值。 @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”类型的异常

如何在 Entity Framework Core 2 中播种?