Подтвердить что ты не робот

Entity Framework 4 - AddObject vs Attach

Недавно я работал с Entity Framework 4 и немного смущен, когда следует использовать ObjectSet.Attach и ObjectSet.AddObject.

Из моего понимания:

  • Используйте "Прикрепить", когда объект уже существует в системе
  • Используйте "AddObject" при создании нового Entity

Итак, если я создаю новый 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) и Unit of Work (не занимайтесь ctx.SaveChanges). Но "под обложками", это то, что происходит в моей реализации.

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

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

ИЗМЕНИТЬ

Чтобы уточнить, я ищу примеры того, когда использовать Attach над 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();

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

4b9b3361

Ответ 1

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

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

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

Например, предположим, что ваш объект Person имеет свойство навигации "Адреса", которое представляет собой коллекцию объекта 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();

Ответ 2

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

В принципе, "отключенный" объект может произойти, когда вы манипулируете сущностью вне области "использования" .

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" будет отключена, поскольку она принадлежит к предыдущей области "использования" , и поскольку предыдущая область "использования" уничтожена, "e" отключается.

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

Ответ 3

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

Ответ 4

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

то есть:

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