Лекции / Глава 17.1 Entity Framework
.pdfОСНОВНЫЕ ОПЕРАЦИИ С ДАННЫМИ
Большинство операций с данными представляют собой CRUD-
операции (Create, Read, Update, Delete), то есть получение данных, создание,
обновление и удаление. Entity Framework позволяет легко производить данные операции.
Для примеров с операциями возьмем модель User, созданную в предыдущем параграфе:
Листинг 17.7
1 |
public partial class User |
|
2 |
{ |
|
3 |
public int Id { get; set; } |
|
4 |
public string Log { get; set; } |
|
5 |
public string Password { get; set; } |
|
6 |
} |
|
|
И следующий класс контекста данных: |
|
|
Листинг 17.8 |
|
|
|
|
1 |
public partial class UserContainer : DbContext |
|
2 |
{ |
|
3 |
public UserContainer() : base("name=UserContainer") |
|
{} |
|
|
|
|
|
4 |
protected override void |
|
OnModelCreating(DbModelBuilder modelBuilder) |
|
|
|
|
|
5 |
{ |
|
6 |
throw new UnintentionalCodeFirstException(); |
|
7 |
} |
|
8 |
public virtual DbSet<User> Users { get; set; } |
|
9 |
} |
|
|
Добавление (CREATE) |
Создадим форму добавления новых объектов:
Рисунок 1
|
Для добавления применяется метод Add() у объекта DbSet: |
|
|
Листинг 17.9 |
|
|
|
|
1 |
using (UserContainer db = new UserContainer()) |
|
2 |
{ |
|
3 |
User user = new User() { Log = textBoxLog.Text, |
|
Password = textBoxPassword.Text }; |
|
|
|
|
|
4 |
db.Users.Add(user); |
|
5 |
db.SaveChanges(); |
|
6 |
} |
|
7 |
listBoxUsers.Items.Clear(); |
|
8 |
using (UserContainer db = new UserContainer()) |
|
9 |
{ |
|
10 |
foreach (var item in db.Users) |
|
11 |
{ |
|
12 |
listBoxUsers.Items.Add(item.Log); |
|
13 |
} |
|
14 |
} |
|
После добавления надо сохранить все изменения с помощью метода
SaveChanges(). В строках 7-13 происходит обновление списка – в него загружаются все элементы из таблицы Users.
Рисунок 2
Обновление (UPDATE)
Создадим форму редактирования объектов, хранящихся в таблице
Users:
Рисунок 3
Контекст данных способен отслеживать изменения объектов, поэтому для редактирования объекта достаточно изменить его свойства и после этого вызвать метод SaveChanges():
|
Листинг 17.10 |
|
|
|
|
1 |
User user = null; |
|
|
|
|
2 |
using (UserContainer db = new UserContainer()) |
|
|
|
|
3 |
{ |
|
|
|
|
4 |
foreach (var item in db.Users) |
|
|
|
|
5 |
{ |
|
|
|
|
6 |
if (item.Log == (string)listBoxUsers.SelectedItem) |
|
|
|
|
7 |
{ |
|
|
|
|
8 |
user = db.Users.Find(item.Id); |
|
|
|
|
9 |
} |
|
|
|
|
10 |
} |
|
|
|
|
11 |
user.Log = textBoxNewLog.Text; |
|
|
|
|
12 |
user.Password = textBoxNewPassword.Text; |
|
|
|
|
13 |
db.SaveChanges(); |
|
|
|
|
14 |
} |
|
Здесь user – объект, который выбирается из списка listBoxUsers для дальнейшего редактирования.
Для поиска искомого объекта в базе данных используется метод Find
класса DbSet, использующий в качестве параметра значение первичного ключа, чтобы найти сущность, отслеживаемую контекстом данных. Если сущность не найдена в контексте, запрос отправляется в базу данных для поиска сущности там. Если сущность не найдена в контексте или в базе данных, возвращается значение NULL.
Рассмотрим ситуацию, где объект User будет получен в одном контексте, а изменять свои значения будет для другого контекста, который его не отслеживает:
|
Листинг 17.11 |
|
|
|
|
1 |
User user = null; |
|
2 |
using (UserContainer db = new UserContainer()) |
|
3 |
{ |
|
4 |
foreach (var item in db.Users) |
|
5 |
{ |
|
6 |
if (item.Log == (string)listBoxUsers.SelectedItem) |
|
7 |
{ |
|
8 |
user = db.Users.Find(item.Id); |
|
9 |
} |
|
10 |
} |
|
11 |
} |
|
12 |
using (UserContainer db = new UserContainer()) |
|
13 |
{ |
|
14 |
user.Log = textBoxNewLog.Text; |
|
15 |
user.Password = textBoxNewPassword.Text; |
|
16 |
db.SaveChanges(); |
|
17 |
} |
|
|
В итоге при попытке обновления изменения не сохранятся. Чтобы |
изменения сохранились, нам явным образом надо установить для состояния объекта значение EntityState.Modified:
|
Листинг 17.12 |
|
|
|
|
1 |
using (UserContainer db = new UserContainer()) |
|
2 |
{ |
|
3 |
user.Log = textBoxNewLog.Text; |
|
4 |
user.Password = textBoxNewPassword.Text; |
|
5 |
db.Entry(user).State = EntityState.Modified; |
|
6 |
db.SaveChanges(); |
|
7 |
} |
|
|
Метод Entry получает объект DbEntityEntry для данной сущности из |
контекста UserContainer, предоставляющий доступ к информации о сущности и возможность выполнять действия с этой сущностью. В нашем случае с помощью свойства State изменяется состояние измененной
сущности для возможности присоединения ее к контексту и дальнейшей ее отправки в базу данных при вызове метода SaveChanges.
Удаление (DELETE)
Создадим форму удаления объектов, хранящихся в таблице Users:
|
Рисунок 4 |
|
|
Для удаления объекта применяется метод Remove() объекта DbSet: |
|
|
Листинг 17.13 |
|
|
|
|
1 |
User user = null; |
|
2 |
using (UserContainer db = new UserContainer()) |
|
3 |
{ |
|
4 |
foreach (var item in db.Users) |
|
5 |
{ |
|
6 |
if (item.Log == (string)listBoxUsers.SelectedItem) |
|
7 |
{ |
|
8 |
user = db.Users.Find(item.Id); |
|
9 |
} |
|
10 |
} |
|
11 |
db.Users.Remove(user); |
|
12 |
db.SaveChanges(); |
|
13 |
} |
|
Метод Remove помечает сущность, переданную в параметры, как удаленную, так что она будет удалена из базы данных при вызове
SaveChanges. Обратите внимание, что перед вызовом этого метода объект должен существовать в контексте в каком-то другом состоянии.
Но, как и в случае с обновлением здесь мы можем столкнуться с похожей проблемой, когда объект получаем из базы данных в пределах одного контекста, а пытаемся удалить в другом контексте. И в этом случае нам надо установить вручную у объекта состояние EntityState.Deleted:
|
Листинг 17.14 |
|
|
|
|
1 |
using(UserContainer db = new UserContainer()) |
|
2 |
{ |
|
3 |
db.Entry(user).State = EntityState.Deleted; |
|
4 |
db.SaveChanges(); |
|
5 |
} |
|
В данном случае состояние сущности изменяется на “Удалено” для ее удаления из базы данных и отсоединения из контекста после вызова метода
SaveChanges.
Присоединение существующей сущности к контексту
Если у вас есть сущность, которая уже существует в базе данных, но в настоящий момент не отслеживается контекстом, можно указать контексту отслеживание сущности с помощью метода Attach в DbSet. Сущность будет находиться в неизмененном состоянии в контексте.
Например:
Листинг 17.15
1 |
User user = new User() { Log = textBoxLog.Text, Password = |
textBoxPassword.Text }; |
|
2 |
using (UserContainer db = new UserContainer()) |
3 |
{ |
4 |
db.Users.Attach(user); |
5 |
string tempLog = db.Entry(user).Entity.Log; |
6 |
db.Entry(user).Entity.Log = "Пользователь "; |
7 |
db.Entry(user).Entity.Log += tempLog; |
8 |
db.Users.Add(db.Entry(user).Entity); |
9 |
db.SaveChanges(); |
10}
Встроке 4 объект user помещается в контекст данных UserContainer
внеизмененном состоянии. Далее, в строке 5 в строковую переменную
tempLog сохраняется логин, полученный от объекта, который был ранее помещен в контекст. В строках 6-7 происходит изменение структуры логина – в начало помещается слово “Пользователь”, после которого идет старый логин, записанный в поле ввода при создании пользователя. После этого, в
строке 8, происходит добавление объекта в контекст для дальнейшего добавления в базу данных после вызова метода SaveChanges.
Обратите внимание, что в базу данных не вносятся изменения, если метод SaveChanges вызывается без выполнения других манипуляций с присоединенной сущностью. Это вызвано тем, что сущность находится в
неизмененном состоянии.