3

是否有最佳做法来制作实体副本,根据用户输入对其进行一些更改,然后将其重新插入到数据库中?EntityFramework Core - 复制实体并将其放回数据库

一些其他的Stackoverflow线程已经提到EF会为你插入新的对象,即使数据库中存在相同的主键,但我不太确定这是EF Core如何处理它。每当我试图复制一个对象,我得到的

Cannot insert explicit value for identity column in table when IDENTITY_INSERT is set to OFF 

基本上,我只需要一个干净的方式来备份复制的对象,使基于用户输入了一些更改,然后插入复制到数据库中的错误,并有正确的Id自动递增。有没有最好的做法或简单的方法来做到这一点,而不必手动设置属性为空或空?

编辑:

public Incident GetIncidentByIdForCloning(int id) 
    { 
     try 
     { 
      return _context.Incident.Single(i => i.IncidentId == id); 
     } 
     catch 
     { 
      return null; 
     } 
    } 

代码检索对象(作为某些字段等RowVersion这是一个时间戳自动产生)后:用于从数据库中检索对象的示例代码

public IActionResult Clone([FromBody]Incident Incident) 
    { 
     var incidentToCopy = _incidentService.IncidentRepository.GetIncidentByIdForCloning(Incident.IncidentId); 
     incidentToCopy.IncidentTrackingRefId = _incidentService.IncidentRepository.GetNextIdForIncidentCategoryAndType(
      Incident.IncidentCategoryLookupTableId, Incident.IncidentTypeLookupTableId).GetValueOrDefault(0); 
     incidentToCopy.RowVersion = null; 
     incidentToCopy.IncidentId = 0; //This will fail with or without this line, this was more of a test to see if manually setting would default the insert operation, such as creating a brand new object would normally do. 
     incidentToCopy.IncidentCategoryLookupTableId = Incident.IncidentCategoryLookupTableId; 
     incidentToCopy.IncidentTypeLookupTableId = Incident.IncidentTypeLookupTableId; 
     var newIncident = _incidentService.IncidentRepository.CreateIncident(incidentToCopy); 
... 

我意识到我可以制作一个全新的对象并进行左手复制,但这看起来非常低效,我想知道EF Core是否提供了更好的解决方案。

+0

[IDENTITY \ _INSERT设置为OFF时无法在表'表中为标识列插入显式值]的可能重复(http://stackoverflow.com/questions/1334012/cannot-insert-explicit-value-for -identity-column-in-table-table-when-identity) – Curiousdev

+0

你可以分享检索实体的代码,并在修改后尝试插入到dba中吗?基本上在错误的基础上,我会建议将id属性设置为默认为零或null,然后将其重新插入到数据库。 –

+0

我试着将它设置为0,当它试图插入错误仍然抛出,并在本地检查它给-2147483647(我假设来自数据库错误的插入)。我将在一秒钟内在我的OP中编辑一些代码信息。至于好奇的开发者,我宁愿不要永远设置IDENTITY_INSERT,因为这最终会在生产环境中出现,而且如果出现问题,这似乎是一个非常大的潜在蠕虫病毒。 –

回答

3

所以我通过“可能重复”去线程,比我当我建立这个主题前最初偶然发现了它,并有一个不那么高upvoted的解决方案,我忽略了本质上多一点当从数据库中检索对象时,它只是一次抓取所有的值 - 并且它不会在流程中检索对该对象的引用。我的代码现在看起来是这样的:

try 
{ 
    var incidentToCopy = _context.Incident.Single(i => i.IncidentId == id); 
    return (Incident) _context.Entry(incidentToCopy).CurrentValues.ToObject(); 
} 
2

在您的IncidentRepository班级中,尝试使用AsNoTracking获取Incident,并且在添加它时应该将其作为新实体进行跟踪。

public void Clone(int id) 
{ 
    // Prevent tracking changes to the object. 
    var incident = _context.AsNoTracking().SingleOrDefault(i => i.Id == id); 

    // Setting back to 0 should treat the object Id as unset. 
    incident.Id = 0; 

    // Add the Incident while it is untracked will treat it as a new entity. 
    _context.Incidents.Add(incident); 
    _context.SaveChanges(); 
} 
+0

我相信这会在尝试将其插入数据库时​​造成问题,因为它知道它没有被跟踪,并且不那么喜欢。我继续前进并抓取了我的检索方法中的对象的所有CurrentValues,现在它似乎正在工作(请参阅我对此线程的回复以获取更多信息) –

+0

@RobertMcCoy DbContext不知道未跟踪的实体。调用添加可以对实体进行跟踪,以便将它排入队列以添加到数据库中。您在运行代码时收到了什么异常? – dkmann

0

我相信这里发生了什么如下:

当你从数据库中检索的值,它被保存在类似

context.ChangeTracker.Entries<Incident> 

这是正在跟踪的事件条目的集合。当你改变你检索到的事件对象的id属性时,你会以效率的名义滥用ChangeTracker。 ChangeTracker不相信你已经创建了一个新的对象。你可能可以尝试类似于在ChangeTracker中找到条目的东西,并将它的状态设置为detached,然后在将id设置为0之后将对象添加回context.DbSet中,但此时您可能已经做出了更多的事情比简单地复制对象复杂。