Работа с состояниями сущностей

В этом разделе описано, как добавлять и присоединять сущности к контексту и как Entity Framework обрабатывает эти сущности во время SaveChanges. Entity Framework заботится о отслеживании состояния сущностей во время подключения к контексту, но в отключенных или N-уровнях сценариев вы можете сообщить EF, в каком состоянии должны находиться ваши сущности. Методы, представленные в этом разделе, также применимы к моделям, созданным с помощью Code First и конструктора EF.

Состояния сущностей и SaveChanges

Сущность может находиться в одном из пяти состояний, определенных перечислением EntityState. Возможны следующие состояния:

  • Добавлено: сущность отслеживается контекстом, но еще не существует в базе данных
  • Без изменений: сущность отслеживается контекстом и существует в базе данных, а ее значения свойств не изменились с значений в базе данных.
  • Изменено: сущность отслеживается контекстом и существует в базе данных, а некоторые или все его значения свойств были изменены.
  • Удалено: сущность отслеживается контекстом и существует в базе данных, но помечена для удаления из базы данных при следующем вызове SaveChanges
  • Отсоединение: сущность не отслеживается контекстом

SaveChanges выполняет различные действия для сущностей в разных состояниях:

  • Неизменяемые сущности не касаются SaveChanges. Обновления не отправляются в базу данных для сущностей в неизменном состоянии.
  • Добавленные сущности вставляются в базу данных, а затем становятся неизменными при возврате SaveChanges.
  • Измененные сущности обновляются в базе данных, а затем становятся неизменными при возврате SaveChanges.
  • Удаленные сущности удаляются из базы данных и затем отсоединяются от контекста.

В следующих примерах показаны способы изменения состояния сущности или графа сущностей.

Добавление новой сущности в контекст

Новую сущность можно добавить в контекст, вызвав метод Add в DbSet. При этом сущность помещается в состояние "Добавлено", то есть она будет вставлена в базу данных при следующем вызове SaveChanges. Например:

using (var context = new BloggingContext())
{
    var blog = new Blog { Name = "ADO.NET Blog" };
    context.Blogs.Add(blog);
    context.SaveChanges();
}

Другим способом добавления новой сущности в контекст является изменение его состояния на Added. Например:

using (var context = new BloggingContext())
{
    var blog = new Blog { Name = "ADO.NET Blog" };
    context.Entry(blog).State = EntityState.Added;
    context.SaveChanges();
}

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

using (var context = new BloggingContext())
{
    // Add a new User by setting a reference from a tracked Blog
    var blog = context.Blogs.Find(1);
    blog.Owner = new User { UserName = "johndoe1987" };

    // Add a new Post by adding to the collection of a tracked Blog
    blog.Posts.Add(new Post { Name = "How to Add Entities" });

    context.SaveChanges();
}

Обратите внимание, что для всех этих примеров, если добавленная сущность содержит ссылки на другие сущности, которые еще не отслеживаются, эти новые сущности также будут добавлены в контекст и будут вставлены в базу данных при следующем вызове SaveChanges.

Присоединение существующей сущности к контексту

Если у вас уже есть сущность, которая уже существует в базе данных, но которая в настоящее время не отслеживается контекстом, можно определить контекст для отслеживания сущности с помощью метода Attach в DbSet. Сущность будет находиться в неизменном состоянии в контексте. Например:

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };

using (var context = new BloggingContext())
{
    context.Blogs.Attach(existingBlog);

    // Do some more work...  

    context.SaveChanges();
}

Обратите внимание, что никакие изменения не будут вноситься в базу данных, если saveChanges вызывается без каких-либо других операций с присоединенной сущностью. Это связано с тем, что сущность находится в состоянии "Без изменений".

Другим способом подключения существующей сущности к контексту является изменение состояния на "Без изменений". Например:

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };

using (var context = new BloggingContext())
{
    context.Entry(existingBlog).State = EntityState.Unchanged;

    // Do some more work...  

    context.SaveChanges();
}

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

Присоединение существующей, но измененной сущности к контексту

Если у вас уже есть сущность, которая уже существует в базе данных, но к которой могут быть внесены изменения, можно сообщить контексту, чтобы присоединить сущность и задать ее состояние в значение "Изменить". Например:

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };

using (var context = new BloggingContext())
{
    context.Entry(existingBlog).State = EntityState.Modified;

    // Do some more work...  

    context.SaveChanges();
}

При изменении состояния на "Изменить" все свойства сущности будут помечены как измененные, и все значения свойств будут отправлены в базу данных при вызове SaveChanges.

Обратите внимание, что если присоединенная сущность содержит ссылки на другие сущности, которые еще не отслеживаются, эти новые сущности будут присоединены к контексту в без изменений, они не будут автоматически изменены. Если у вас есть несколько сущностей, которые необходимо пометить "Изменить", необходимо задать состояние для каждой из этих сущностей по отдельности.

Изменение состояния отслеживаемой сущности

Вы можете изменить состояние сущности, которая уже отслеживается, задав свойство State для его записи. Например:

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };

using (var context = new BloggingContext())
{
    context.Blogs.Attach(existingBlog);
    context.Entry(existingBlog).State = EntityState.Unchanged;

    // Do some more work...  

    context.SaveChanges();
}

Обратите внимание, что для изменения состояния сущности можно также использовать вызов "Добавить или подключить" для уже отслеживаемой сущности. Например, вызов присоединения для сущности, которая в настоящее время находится в добавленном состоянии, изменит его состояние на "Без изменений".

Вставка или обновление шаблона

Распространенный шаблон для некоторых приложений заключается в добавлении сущности в качестве новой (в результате вставки базы данных) или присоединению сущности как существующей и помечать ее как измененную (в результате обновления базы данных) в зависимости от значения первичного ключа. Например, при использовании созданных в базе данных первичных ключей целочисленного числа обычно рассматривается сущность с нулевым ключом как новой и сущностью с ненулевым ключом как существующей. Этот шаблон можно достичь, задав состояние сущности на основе проверка значения первичного ключа. Например:

public void InsertOrUpdate(Blog blog)
{
    using (var context = new BloggingContext())
    {
        context.Entry(blog).State = blog.BlogId == 0 ?
                                   EntityState.Added :
                                   EntityState.Modified;

        context.SaveChanges();
    }
}

Обратите внимание, что при изменении состояния на "Изменить" все свойства сущности будут помечены как измененные, а все значения свойств будут отправляться в базу данных при вызове SaveChanges.