实体框架4 - AddObject与Attach

时间:2010-10-13 00:59:05

标签: c# .net entity-framework-4 crud

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

根据我的理解:

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

所以,如果我创建一个新人,我会这样做。

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)。但是“在幕后”,上面是我的实施中发生的事情。

现在,我的问题 - 我还没有找到一个必须使用附加的方案。

我在这里缺少什么?我们什么时候需要使用Attach?

修改

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

编辑2

以下答案是正确的(我接受了),但我想添加另一个例子,其中Attach会有用。

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

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

如果(出于某种原因),我已经知道系统中存在“Joe Bloggs”,为什么还需要额外的查询才能获得他的第一个?我能做到这一点:

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

这将导致只执行UPDATE语句。

5 个答案:

答案 0 :(得分:154)

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

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

此外,使用Attach方法,您可以定义ObjectContext中已存在但 已自动连接的实体之间的关系。基本上,Attach的主要目的是连接已经附加到ObjectContext并且 new的实体,因此您不能使用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();

答案 1 :(得分:27)

这是一个迟到的回复,但它可能会帮助其他人找到这个。

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

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”被断开。

这就是我理解的方式。

答案 2 :(得分:7)

这是Programming Entity Framework: DbContext

的引用
  

在实体上调用删除   上下文不跟踪将导致抛出InvalidOperationException。该   实体框架抛出此异常,因为不清楚您是否是实体   试图删除是一个应标记为删除或新的实体   应该被忽略的实体。出于这个原因,我们不能只使用Remove来标记a   已断开连接的实体已删除;我们需要先贴上

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方法来允许上下文   知道这是一个现有的目的地。然后使用Remove方法注册   现有的删除目的地

答案 3 :(得分:-1)

我用过这个方法

<块引用>
var user = _context.Users.Attach(new User
     {
     Name = "Fahimeh",
     Email = "text@mail.com",
});
 _context.SaveChanges();

 return View(user);

答案 4 :(得分:-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();