Entity Framework 4 - AddObject vs Attach


132

Я працював з Entity Framework 4 останнім часом, і я трохи розгублений, коли використовувати ObjectSet.Attach та ObjectSet.AddObject .

З мого розуміння:

  • Використовуйте "Attach", коли Entity вже існує в системі
  • Використовуйте "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();

Майте на увазі, це дуже простий приклад. Насправді я використовую чисті POCO (без генерації коду), шаблон сховища (не займайтеся ctx.Persons) та Unit Work (не працюйте з ctx.SaveChanges). Але "під прикриттями", вище, що відбувається в моїй реалізації.

Тепер моє запитання - я ще не знаходжу сценарій, де мені довелося б використовувати Attach .

Чого я тут пропускаю? Коли нам потрібно використовувати Attach?

EDIT

Просто для уточнення, я шукаю приклади використання Attach over AddObject (або навпаки).

EDIT 2

Нижче наведена відповідь є правильною (що я прийняв), але я подумав, що я додаю ще один приклад, де Attach буде корисним.

У моєму вище прикладі для зміни наявної особи , насправді виконується два запити.

Один для отримання персоналу (.SingleOrDefault), а інший для оновлення UPDATE (.SaveChanges).

Якщо я (чомусь чомусь) я вже знав, що "Джо Блогги" існує в системі, навіщо робити додатковий запит, щоб отримати його першим? Я міг би це зробити:

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

Це призведе до виконання оператора UPDATE.


Attach також використовується в MVC зараз у дні, коли повертати моделі безпосередньо в EF. Працює досить добре і економить тону кодових ліній.
Пьотр Кула

Відповіді:


162

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

ObjectContext.Attach і ObjectSet.Attach :
З іншого боку, Attach використовується для об'єктів, які вже є в базі даних. Замість того, щоб встановити EntityState в доданий, Attach приводить до незмінного EntityState, а це означає, що він не змінився з моменту приєднання до контексту. Передбачається, що об’єкти, які ви додаєте, існують у базі даних. Якщо ви змінюєте об'єкти після їх додавання, під час виклику SaveChanges значення EntityKey використовується для оновлення (або видалення) відповідного рядка шляхом пошуку його відповідного ідентифікатора в таблиці db.

Крім того, використовуючи метод Attach, ви можете визначити відносини між сутностями, які вже існують у ObjectContext, але маютьне підключено автоматично. В основному основною метою Attach є з'єднання сутностей, які вже приєднані до ObjectContext і не єновими, тому ви не можете використовувати Attach для приєднання сутностей, EntityState додано. Уцьому випадкуви повинні використовувати Add () .

Наприклад, припустимо, що у вашої особи Person є властивість навігації під назвою Addresses, яка є сукупністюсутності адреси . Скажімо, ви читали обидва об’єкти з контексту, але вони не пов'язані один з одним, і ви хочете зробити так:

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

Дякую за відповідь, я розумію визначення двох (він же перших двох абзаців). Але я не розумію сценарій, коли мені НЕОБХІДНО використовувати Attach. Ваш останній абзац насправді не має для мене сенсу (читається в основному як комбінація перших двох абзаців), ви можете надати мені приклад того, де я б використав "Attach" у своєму вищевикладеному сценарії? Це дійсно те, що я шукаю - приклади, а не визначення. Хоча дуже ціную свій час. :)
RPM1984

1
Немає проблем, я додав фрагмент коду для уточнення останнього абзацу, оскільки ви бачите, що у нас є 2 неспоріднених об’єкта, і додавання допомагає нам пов’язати їх один з одним. Іншим прикладом може бути використання методу Attach () для приєднання "Окремої сутності" назад до контексту (Є різні причини, за якими, можливо, ви хочете, щоб відокремлене об'єднання було приєднане назад до контексту)
Мортеза Манаві

1
Так, я зараз. Я щойно переглядав TechEd vid на EF4 (від Джулі Лерман), який показав приклад. Можливо, у вас є сутність, яку ви НЕ ВІДКЛЮЧАЄТЬ із запиту (тобто вона була відновлена), але ви знаєте, що вона існує, тому ви використовуєте Attach, щоб виконати ОНОВЛЕННЯ для цієї сутності. Має сенс, хоча я все ще намагаюся розробити сценарій, де у вас буде "відключена" сутність. Спасибі за вашу допомогу.
RPM1984

1
Чудово. Чи можу я попросити вас поділитися посиланням на відео для інших розробників, які можуть прочитати цю публікацію?
Мортеза Манаві

3
Посилання, яке надсилається RPM1984, перерване, перенаправлено зараз на канал9.msdn.com/Events/TechEd/NorthAmerica/2010/DEV205 . Насолоджуйтесь
стек

31

Це пізня відповідь, але це може допомогти іншим, хто це знайде.

В основному, "відключений" об'єкт може статися, коли ви маніпулюєте суттю поза сферою "використання"

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

Ось як я це розумію.


3
Приклад Tchi - чудовий і простий приклад - так, змінна Employee повинна бути оголошена зовні. спробуйте e.Address.Street за межами області та побачте спливаюче вікно виключення з нульовою посиланням. Якщо Ви додаєте, тоді додатку не доведеться повертатися до БД для працівника у другій області.
Стів

9

Це цитата з програми програмного забезпечення: DbContext

Виклик Remove на об'єкт, який не відстежується контекстом, призведе до перекидання InvalidOperationException. Entity Framework викидає це виняток, оскільки незрозуміло, чи є об'єкт, який ви намагаєтесь видалити, існуючим об'єктом, який слід позначити для видалення, або новим об'єктом, який слід просто ігнорувати. З цієї причини ми не можемо використовувати просто 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();
    }
}

Метод TestDeleteDestination імітує клієнтську програму, яка отримує наявне призначення з сервера та передає його методу DeleteDestination на сервері. Метод DeleteDestination використовує метод Attach, щоб повідомити контексту, що це вже існуюче призначення. Тоді метод Remove використовується для реєстрації існуючого пункту призначення для видалення


-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();
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.