entity framework4 – AddObject与附加

最近我一直在使用Entity Framework 4,并且对于何时使用ObjectSet.Attach和ObjectSet.AddObject有些困惑。

从我的理解:

  • 当实体已经存在于系统中时使用“附加”
  • 创build一个全新的实体时使用“AddObject”

所以,如果我正在创build一个新的人 ,我这样做。

var ctx = new MyEntities(); var newPerson = new Person { Name = "Joe Bloggs" }; ctx.Persons.AddObject(newPerson); ctx.SaveChanges(); 

如果我正在修改现有的人 ,我这样做:

 var ctx = new MyEntities(); var existingPerson = ctx.Persons.SingleOrDefault(p => p.Name = "Joe Bloggs" }; existingPerson.Name = "Joe Briggs"; ctx.SaveChanges(); 

请记住,这是一个非常简单的例子。 实际上,我使用的是Pure POCO(无代码生成),Repository模式(不处理ctx.Persons)和Unit of Work(不处理ctx.SaveChanges)。 但是“下面”,以上就是我执行中发生的事情。

现在,我的问题 – 我还没有find一个情况,我不得不使用附加

我在这里错过了什么? 我们什么时候需要使用Attach?

编辑

只是为了澄清,我正在寻找什么时候使用附加的AddObject(反之亦然)的例子

编辑2

下面的答案是正确的(我接受),但认为我会添加另一个例子附加将是有用的。

在我上面修改现有Person的例子中,两个查询实际上正在执行。

一个检索Person(.SingleOrDefault),另一个执行UPDATE(.SaveChanges)。

如果(出于某种原因),我已经知道系统中存在“Joe Bloggs”,为什么还要额外的查询让他第一个? 我可以这样做:

 var ctx = new MyEntities(); var existingPerson = new Person { Name = "Joe Bloggs" }; ctx.Persons.Attach(existingPerson); ctx.SaveChanges(); 

这将导致只执行UPDATE语句。

ObjectContext.AddObjectObjectSet.AddObject
AddObject方法用于添加数据库中存在的新创build的对象。 实体将获得一个自动生成的临时EntityKey,并将其EntityState设置为Added 。 当调用SaveChanges时,EF将清楚该实体需要插入到数据库中。

ObjectContext.AttachObjectSet.Attach
另一方面, Attach用于数据库中已经存在的实体。 将结果添加到未更改的 EntityState中,而不是将EntityState设置为已添加,这意味着它自从附加到上下文后没有更改。 假定您正在附加的对象存在于数据库中。 如果在对象被修改后修改对象,当调用SaveChanges时,EntityKey的值将用于通过在db表中查找匹配的ID来更新(或删除)适当的行。

此外,使用Attach方法,您可以定义ObjectContext中已经存在但尚未自动连接的实体之间的关系。 基本上,Attach的主要目的是连接已经连接到ObjectContext并且不是新的实体,所以你不能使用Attach来附加其EntityState被添加的实体。 在这种情况下你必须使用Add()

例如,假设您的Person实体具有一个名为Addresses的导航属性,它是Address实体的集合。 假设您已经从上下文中读取了两个对象,但是它们并没有相互关联,而是想要这样做:

 var existingPerson = ctx.Persons.SingleOrDefault(p => p.Name = "Joe Bloggs" }; var myAddress = ctx.Addresses.First(a => a.PersonID != existingPerson.PersonID); existingPerson.Addresses.Attach(myAddress); // OR: myAddress.PersonReference.Attach(existingPerson) ctx.SaveChanges(); 

这是一个迟到的反应,但它可能会帮助其他人发现这一点。

基本上,当您操作“使用”范围之外的实体时,可能会发生“断开连接”的实体。

 Employee e = null; using (var ctx = new MyModelContainer()) { e = ctx.Employees.SingleOrDefault(emp => emp .....); } using (var ctx2 = new MyModelContainer()) { e; // This entity instance is disconnected from ctx2 } 

如果你input另一个“使用”范围,那么“e”variables将被断开,因为它属于前一个“使用”范围,并且由于之前的“使用”范围被销毁,所以“e”被断开。

这就是我的理解。

这是编程entity framework:DbContext的引用

在没有被上下文跟踪的实体上调用Remove将导致抛出InvalidOperationExceptionexception。 entity framework抛出这个exception,因为不清楚你试图删除的实体是一个应该被标记为删除的现有实体还是一个应该被忽略的新实体。 出于这个原因, 我们不能只使用Remove来将已断开的实体标记为已删除; 我们需要先附上它

 private static void TestDeleteDestination() { Destination canyon; using (var context = new BreakAwayContext()) { canyon = (from d in context.Destinations where d.Name == "Grand Canyon" select d).Single(); } DeleteDestination(canyon); } private static void DeleteDestination(Destination destination) { using (var context = new BreakAwayContext()) { context.Destinations.Attach(destination); context.Destinations.Remove(destination); context.SaveChanges(); } } 

TestDeleteDestination方法模拟客户端应用程序从服务器获取现有目标,然后将其传递给服务器上的DeleteDestination方法。 DeleteDestination方法使用Attach方法让上下文知道它是一个现有的Destination。 然后,删除方法用于注册现有的目标删除

那么只参考主键而不是附加?

即:

 var existingPerson = ctx.Persons.SingleOrDefault(p => p.Name = "Joe Bloggs" }; var myAddress = ctx.Addresses.First(a => a.PersonID != existingPerson.PersonID); existingPerson.AddressId = myAddress.Id // not -> existingPerson.Addresses.Attach(myAddress); // OR: myAddress.Person.Id = existingPerson.Id // not -> myAddress.PersonReference.Attach(existingPerson); ctx.SaveChanges();