Asp.net 实体框架如何决定是引用现有对象还是创建新对象?

Asp.net 实体框架如何决定是引用现有对象还是创建新对象?,asp.net,asp.net-mvc,entity-framework,asp.net-mvc-4,entity-framework-5,Asp.net,Asp.net Mvc,Entity Framework,Asp.net Mvc 4,Entity Framework 5,只是出于好奇(以及将来的知识),EntityFramework5是如何决定何时创建一个新对象而不是引用一个现有对象的?我可能只是做错了什么,但如果我时不时地做一些事情: using (TestDB db = new TestDB()) { var currParent = db.Parents.Where(p => p.Prop == passedProp).FirstOrDefault(); if(currParent == null) { Parent

只是出于好奇(以及将来的知识),EntityFramework5是如何决定何时创建一个新对象而不是引用一个现有对象的?我可能只是做错了什么,但如果我时不时地做一些事情:

using (TestDB db = new TestDB())
{
    var currParent = db.Parents.Where(p => p.Prop == passedProp).FirstOrDefault();
    if(currParent == null) {
         Parent newParent = new Parent();
         newParent.Prop = passedProp;
         currParent = newParent;
    }
    //maybe do something to currParent here
    var currThing = db.Things.Where(t => t.Prop == passedPropTwo).FirstOrDefault();
    currThing.Parent = currParent;
    db.SaveChanges();
}
EF将在数据库中创建一个新的父项,基本上是currParent的副本,然后将currThing的Parent_ID值设置为该副本。然后,如果我再做一次(比如,如果已经有两个这样的父母),它将不会生成新的父母,而是链接到第一个。我真的不理解这种行为,但在玩了一段时间后,我想:

using (TestDB db = new TestDB())
{
    var currParent = db.Parents.Where(p => p.Prop == passedProp).FirstOrDefault();
    if(currParent == null) {
         Parent newParent = new Parent();
         newParent.Prop = passedProp;
         currParent = newParent;
    }
    //maybe do something to currParent here
    var currThing = db.Things.Where(t => t.Prop == passedPropTwo).FirstOrDefault();
    currThing.Parent = db.Parents.Where(p => p.ID == currParent.ID).First();
    db.SaveChanges();
}

似乎解决了这个问题。有什么原因可能会发生这种情况,我应该知道,或者只是有什么奇怪的方式,我在做这件事的时候?很抱歉,我不能更具体地说明确切的代码是什么,我前一段时间遇到了这个问题,并用上面的代码修复了它,所以我看不出有任何理由询问它。更一般地说,EF如何决定是否引用现有项而不是创建新项?仅基于是否设置了ID?谢谢

如果您的
DBContext
的特定实例向您提供了该实体的特定实例,则它将知道它代表的是数据库中的哪些记录,并且您对其所做的任何更改都将适用于数据库中的该(些)记录。如果您自己实例化一个新实体,那么您需要告诉
DBContext
如果该记录不是应该插入数据库的新记录,那么该记录到底是什么

在特殊情况下,如果您有多个
DBContext
实例,其中一个实例为您提供了此实体,但您希望使用另一个实例来处理和保存该实体,那么您必须使用
((IObjectContextAdapter)firstDbContext).ObjectContext.Detach()
孤立此实体,然后使用
((IObjectContextAdapter)secondDbContext).ObjectContext.Parents.Attach()
附加它(或者
ApplyChanges()
如果您也在编辑它-这将为您调用
Attach

在其他一些特殊场景中(对象已序列化和/或具有自跟踪实体),可能需要执行一些附加步骤,具体取决于您正试图执行的操作



总之,如果您的
DBContext
的特定实例是“aware”对于实体的特定实例,它将像直接绑定到数据库中的特定行一样使用它。

代码中的
newParent
是什么?它是新实例化的
父对象吗?它是数据库之外的对象吗?这一点很重要。如果currParent为null(AFAIK,这意味着数据库中当前没有一个)。我将在一秒钟内更新代码以回答您的问题,“更一般地说,EF如何决定是否引用现有项而不是创建新项?”,这取决于对象的来源。它是来自数据库还是在内存中实例化?如果
db
对象是“感知的”对于任何实体,在调用
SaveChanges
时,它将继续使用同一实体。但是,它不知道,那么其他一些事情很重要。一般来说,在任何情况下,只设置
currThing.ParentID=currprent.ID
可能最简单。@Jaxidian感谢您提供的信息,但是如果在代码优先模型中,我没有ParentID,我有一个Parent对象?或者你是说currThing.Parent.ID?最终,当你从数据库中选择一个实体时,你的DBContext将保留(在内存中)a/这些实体的一些集合。当您引用该集合中的任何内容时,这有助于DBContext了解如何使用它。其他内容可以与自跟踪实体或EF的一些其他配置相结合。您是否了解了
ApplyChanges
Attach
?另一种方法可能是:
((IObjectContextAdapter)db.ObjectContext.ObjectStateManager.ChangeObjectState(newParent,EntityState.Modified);
谢谢,我最后做了什么(我马上就来试试)was db.Parents.Attach,从描述来看,它似乎也以同样的方式工作哦,是的,你是对的。我为这个错误道歉。我将更新答案以反映这一点。:)