实体框架4-AddObject与附加


132

我最近一直在使用Entity Framework 4,对于何时使用ObjectSet.AttachObjectSet.AddObject感到有些困惑。

据我了解:

  • 当系统中已经存在实体时,使用“附加”
  • 创建全新的实体时使用“ AddObject”

因此,如果我要创建一个新的Person,则可以执行此操作。

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

如果我要修改现有的Person,请执行以下操作:

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

请记住,这是一个非常简单的示例。实际上,我使用的是Pure POCO(不生成代码),存储库模式(不处理ctx.Persons)和工作单元(不处理ctx.SaveChanges)。但是,“在幕后”,以上就是我的实现过程。

现在,我的问题 -我还没有找到必须使用Attach的情况

我在这里想念什么?我们什么时候需要使用Attach?

编辑

只是为了澄清,我正在寻找何时使用AddObject上的Attach的示例(反之亦然)。

编辑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语句。


将模型直接放回EF时,现在在MVC中也使用了Attach。效果很好,并节省了大量的代码行。
Piotr Kula 2014年

Answers:


162

ObjectContext.AddObject ObjectSet.AddObject
ADDOBJECT方法是将那些新创建的对象存在于数据库中。该实体将获得自动生成的临时 EntityKey,其EntityState将设置为 Added。调用SaveChanges时,对于EF来说很清楚,该实体需要插入数据库中。

ObjectContext.Attach ObjectSet.Attach
另一方面, Attach用于数据库中已经存在的实体。Attach不会将EntityState设置为Added,而是会产生 Unchanged 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();

感谢您的回答,我确实理解了两者的定义(又名前两段)。但是我不了解我需要使用Attach的情况。您的最后一段对我来说真的没有意义(基本上就像前两段的结合一样阅读),您能否举一个例子说明在上述情况下我将在何处使用“附加”?我真的在找我-例子,而不是定义。真的很感谢您的时间。:)
RPM1984

1
没问题,我添加了一个代码段来澄清最后一段,因为您可以看到我们有2个不相关的对象,并且Attach帮助我们将它们彼此关联。另一个示例是使用Attach()方法将“分离的实体”附加到上下文(出于各种原因,您可能希望将分离的实体附加回到上下文)
Morteza Manavi 2010年

1
是的,我现在知道了。我刚刚观看了EF4上的TechEd vid(朱莉·莱尔曼(Julie Lerman)),它展示了一个例子。您可能有一个实体,但您没有从查询中检索到该实体(即,该实体是断开连接的),但是您知道它存在,因此您可以使用Attach对那个实体执行UPDATE。是有道理的,尽管我仍在努力设想一个方案,在该方案中您将拥有一个“断开连接”的实体。谢谢你的帮助。
RPM1984

1
大。我可以请您将视频的链接分享给可能碰到这篇文章的其他开发人员吗?
Morteza Manavi 2010年

3
上面由RPM1984共享的链接已损坏,现在已重定向到channel9.msdn.com/Events/TechEd/NorthAmerica/2010/DEV205。享受
堆积

31

这是一个较晚的响应,但可能会帮助发现此问题的其他人。

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

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
}

如果输入另一个“使用”范围,则“ e”变量将被断开连接,因为它属于先前的“使用”范围,并且由于先前的“使用”范围被破坏,因此“ e”将断开连接。

这就是我的理解。


3
Tchi的示例是一个出色而简单的示例-是的,Employee变量应该在外部声明。在范围之外尝试e.Address.Street,然后弹出一个空引用异常。如果您附加,则该应用程序将不必返回第二个作用域中Employee的数据库。
2012年

9

这是来自编程实体框架的报价:DbContext

在上下文未跟踪的实体上调用Remove将导致引发InvalidOperationException。实体框架会引发此异常,因为尚不清楚您要删除的实体是应该标记为删除的现有实体还是应该被忽略的新实体。因此,我们不能仅使用Remove来将断开连接的实体标记为Deleted;我们需要先附加它

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方法模拟客户端应用程序,该客户端应用程序从服务器获取现有的Destination,然后将其传递给服务器上的DeleteDestination方法。DeleteDestination方法使用Attach方法使上下文知道它是现有的Destination。然后,使用Remove方法注册现有的目标以进行删除


-8

只引用主键而不是附加键怎么办?

即:

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();
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.