我最近一直在使用Entity Framework 4,对于何时使用ObjectSet.Attach和ObjectSet.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语句。
答案 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();