Entity Framework 4 - AddObject против Attach - PullRequest
124 голосов
/ 13 октября 2010

Я недавно работал с Entity Framework 4, и меня немного смущает вопрос, когда использовать ObjectSet.Attach и ObjectSet.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 (без генерации кода), шаблон репозитория (не работайте с ctx.Persons) и Unit of Work (не работайте с ctx.SaveChanges). Но "под прикрытием", выше, что происходит в моей реализации.

Теперь мой вопрос - Мне еще предстоит найти сценарий, в котором мне пришлось бы использовать Attach .

Что мне здесь не хватает? Когда нам нужно использовать Attach?

EDIT

Просто чтобы уточнить, я ищу примеров того, когда использовать Attach over AddObject (или наоборот).

РЕДАКТИРОВАТЬ 2

Приведенный ниже ответ верен (что я принял), но подумал, что добавлю еще один пример, в котором будет полезно Attach.

В приведенном выше примере для изменения существующего Person фактически выполняются два запроса.

Один для извлечения человека (.SingleOrDefault), а другой для выполнения ОБНОВЛЕНИЯ (.SaveChanges).

Если (по какой-то причине) я уже знал, что в системе существует «Джо Блоггс», зачем делать дополнительный запрос, чтобы получить его первым? Я мог бы сделать это:

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

Это приведет к выполнению только оператора UPDATE.

Ответы [ 4 ]

154 голосов
/ 13 октября 2010

ObjectContext.AddObject и ObjectSet.AddObject :
AddObject метод для добавленияВ базе данных существуют только что созданные объекты, которые не .Сущность получит автоматически сгенерированный временный EntityKey , а его EntityState будет установлен на Added .Когда вызывается SaveChanges, EF будет ясно, что этот объект нужно вставить в базу данных.

ObjectContext.Attach и ObjectSet.Attach :
С другой стороны, Attach используется для сущностей, которые уже существуют в базе данных.Вместо того, чтобы устанавливать EntityState в значение Added, Attach приводит к Unchanged EntityState, что означает, что оно не изменилось с момента его присоединения к контексту.Предполагается, что присоединяемые объекты существуют в базе данных.Если вы изменяете объекты после того, как они были прикреплены, то при вызове SaveChanges значение EntityKey используется для обновления (или удаления) соответствующей строки путем нахождения ее соответствующего идентификатора в таблице базы данных.

Кроме того, используя метод Attach, вы можете определить отношения между сущностями, которые уже существуют в ObjectContext, но которые не были подключены автоматически.По сути, основная цель Attach - подключить сущности, которые уже присоединены к ObjectContext и являются , а не новыми, поэтому вы не можете использовать Attach для присоединения сущностей, для которых добавлен EntityState.В этом случае вы должны использовать Add () .

Например, предположим, что у вашей сущности Person есть свойство навигации с именем Адреса , которое представляет собой совокупность Address entity.Допустим, вы прочитали оба объекта из контекста, но они не связаны друг с другом, и вы хотите сделать это так:

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();
27 голосов
/ 01 августа 2012

Это поздний ответ, но он может помочь другим, которые его обнаружат.

По сути, «отключенная» сущность может возникнуть, когда вы манипулируете сущностью вне области «использования».

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
}

Если вы введете другую область «using», то переменная «e» будет отключена, поскольку она принадлежит предыдущей области «using», а поскольку предыдущая область «using» уничтожена, тогда «e» отключится.

Вот так я понимаю.

7 голосов
/ 30 июля 2014

Это цитата из Framework Entity Framework: DbContext

Вызов Удалить на объекте, который не отслеживается контекстом, вызывает исключение InvalidOperationException. Entity Framework выдает это исключение, потому что не ясно, является ли объект, который вы пытаются удалить существующий объект, который должен быть помечен для удаления или новый сущность, которую следует просто игнорировать. По этой причине мы не можем использовать просто Удалить, чтобы отметить отключенный объект как удаленный; нам нужно сначала прикрепить его .

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 используется для регистрации существующее назначение для удаления

0 голосов
/ 19 июля 2011

А как насчет ссылки на первичный ключ вместо присоединения?

т.е.:

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();
...