实体框架4 - AddObject与Attach

RPM*_*984 128 .net c# crud entity-framework-4

我最近一直在使用Entity Framework 4,并且稍微混淆了何时使用ObjectSet.AttachObjectSet.AddObject.

根据我的理解:

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

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

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

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

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

请记住,这是一个非常简单的例子.实际上我使用的是Pure POCO(无代码生成),Repository模式(不处理ctx.Persons)和Unit of Work(不处理ctx.SaveChanges).但是"在幕后",以上是我的实施中发生的事情.

现在,我的问题 - 我还没有找到一个我不得不使用Attach的场景.

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

编辑

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

编辑2

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

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

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

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

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

这将导致只执行UPDATE语句.

Mor*_*avi 159

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

ObjectContext.Attach ObjectSet.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();
Run Code Online (Sandbox Code Playgroud)

  • RPM1984共享的上述链接已损坏,现在重定向到https://channel9.msdn.com/Events/TechEd/NorthAmerica/2010/DEV205.请享用 (3认同)

Tch*_*uan 29

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

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

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
}
Run Code Online (Sandbox Code Playgroud)

如果您输入另一个"使用"范围,那么"e"变量将被断开,因为它属于之前的"使用"范围,并且由于先前的"使用"范围被破坏,因此"e"被断开.

这就是我理解它的方式.

  • Tchi的例子是一个很好的简单例子 - 是的,Employee变量应该在外面声明.尝试范围外的e.Address.Street并看到一个空引用异常弹出窗口.如果您连接,那么应用程序将不必返回第二个范围内的Employee的DB. (2认同)

Teo*_*ahi 7

这是编程实体框架:DbContext的引用

在未被上下文跟踪的实体上调用Remove将导致抛出InvalidOperationException.实体框架抛出此异常,因为不清楚您尝试删除的实体是应该标记为删除的现有实体还是应该被忽略的新实体.出于这个原因,我们不能仅使用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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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