在一名员工离开公司后,我继承了一些相当大的代码。不幸的是,该计划在他离开后的第二天就破了任何人都可以指出我在哪里看到以下错误?
附加类型' MasT.DB.jobqueue'的实体失败,因为同一类型的另一个实体已具有相同的主键值。使用'附加'方法或将实体的状态设置为“未更改”#39;或者'修改'如果图中的任何实体具有冲突的键值。这可能是因为某些实体是新的并且尚未收到数据库生成的键值。在这种情况下,请使用'添加'方法或“添加”#39;实体状态跟踪图形,然后将非新实体的状态设置为“未更改”。或者'修改'作为适当的。在
System.Data.Entity.Core.Objects.ObjectContext中的System.Data.Entity.Core.Objects.ObjectContext.VerifyRootForAdd(Boolean doAttach,String entitySetName,IEntityWrapper wrappedEntity,EntityEntry existingEntry,EntitySet& entitySet,Boolean& isNoOperation)。 AttachTo(String entitySetName,Object entity)位于System.Data.Entity.Internal.
1.<>c__DisplayClassa.<Attach>b__9() at System.Data.Entity.Internal.Linq.InternalSet
System.Data.Entity.Internal的1.ActOnSet(Action action,EntityState newState,Object entity,String methodName)。 Linq.InternalSet1.Attach(Object entity) at System.Data.Entity.DbSet
1.Attach(TEntity entity)at DT.ValidatorCore.JobQueue.MasTJobQueueMySql。&lt;&gt; c.b__23_7(jobqueue jq)at System.Collections.Generic.List1.ForEach(Action
1 action )在DT.ValidatorCore.JobQueue.MasTJobQueueMySql.MaintainJobQueue()在DT.ValidatorCore.JobQueue.MasTJobQueueMySql.TakeJobsQueue(布尔includeCompleted)在DT.ValidatorCore.JobQueue.MasTJobQueue.DoWork(字符串日期,布尔testRun,的Int32的runid)在DT。 ValidatorCore.Commands.TriggerCommand.QueueCommand.Process(CmdTrigger1 trigger) at DT.Common.Commands.BaseCommand
1.TriggerSubCommand(CmdTr igger1 trigger) at DT.Common.Commands.Command
1.Process(CmdTrigger1 trigger) at DT.Common.Commands.CommandMgr
1.Execute(CmdTrigger1 trigger, BaseCommand
1 cmd,布尔值silentFail)
我很困惑,因为只有当程序在生产服务器上运行时才会在调试中运行。虽然它们连接到两个单独的数据库,但它们是相同的。
最初我只被要求更新代码的某些部分,所以这是一个很大的跳跃!
我很确定问题与DT.ValidatorCore.JobQueue.MasTJobQueueMySql.MaintainJobQueue
有关,但我对实体框架的了解非常有限
protected void MaintainJobQueue()
{
if (_jobQueueUnitOfWork != null)
_jobQueueUnitOfWork.Dispose();
_jobQueueUnitOfWork = new JobQueueUnitOfWork();
List<jobqueue> tempList = _jobQueueUnitOfWork.JobQueueRepository.GetAll();
if (tempList == null)
return;
tempList.RemoveAll(jqItem => jqItem == null);
tempList.RemoveAll(jqItem => jqItem.packageinfo == null);
tempList.RemoveAll(jqItem => jqItem.packageinfo.pkg_content_id == null);
if (!tempList.Any())
return;
var tempList2 = tempList.GroupBy(g => g.packageinfo.pkg_content_id + g.packageinfo.pkg_master_version + g.packageinfo.app_version).Select(x => x.ToList().OrderByDescending(m => m.packageinfo.app_revision).First()).ToList();
tempList.RemoveAll(i => tempList2.Contains(i));
tempList.ForEach(jq => context.jobqueue.Attach(jq));
var pkgInfoRemovals = tempList.Select(i => i.packageinfo);
_jobQueueUnitOfWork.PackageInfoRepository.DeleteRange(pkgInfoRemovals);
var submissionpathRemovals = tempList.Select(i => i.submissionpath);
context.submissionpath.RemoveRange(submissionpathRemovals);
_jobQueueUnitOfWork.SubmissionPathRepository.DeleteRange(submissionpathRemovals);
_jobQueueUnitOfWork.JobQueueRepository.DeleteRange(tempList);
}
protected override void SaveChanges()
{
_jobQueueUnitOfWork.Save();
}
干杯!
答案 0 :(得分:1)
很难确定您共享的代码似乎是围绕实体框架构建的包装器,因此模糊了一些必要的细节,但有根据的猜测说您正在处理Detached Entities
。
要搜索的关键字是DbContext
(数据库上下文)。
如果您使用实体框架(EF)从数据库中获取某些数据,则此数据仍会附加到数据库或DbContext
,这是attached entity
。
现在,EF会自动跟踪对数据所做的任何更改,因此当您致电SaveChanges()
时,它会知道UPDATE
现有数据。
在您的情况下,我怀疑_jobQueueUnitOfWork.JobQueueRepository.GetAll();
从其他地方(例如Web API)获取数据。由于此数据是在DbContext
之外创建的,因此EF无法知道它所处的状态,这是detached entity
。
解决方案是简单地告诉EF数据处于什么状态,在您修改的情况下,需要UPDATE
超过INSERT
。
tempList.ForEach(jq =>
{
context.jobqueue.Attach(jq); // Attach to `DbContext`
context.Entry(jq).State = System.Data.Entity.EntityState.Modified; // Modified
});
如果您搜索与dbcontext
,change tracking
和attached/detached entities
相关的实体框架文章,它应该回答您的很多问题。