Лекции / Глава 17.2 Entity Framework 2
.pdf4 |
foreach (Professor p in professors) |
5 |
{ |
6 |
if (p.Name == name) |
7 |
{ |
8 |
foreach (Thesis t in p.Theses) |
9 |
{ |
10 |
listBox1.Items.Add(t.Name); |
11 |
} |
12 |
} |
13 |
} |
14 |
} |
Без использования метода Include мы бы не могли бы получить связанные с руководителем дипломные проекты и его свойства.
Явная загрузка
Явная загрузка предусматривает применение метода Load() для загрузки данных в контекст. Например:
|
Листинг 17.21 |
|
|
|
|
1 |
using (UserContext db = new UserContext()) |
|
2 |
{ |
|
3 |
var FirstProfessor = db.Professors.FirstOrDefault(); |
|
4 |
db.Entry(FirstProfessor).Collection("Theses").Load(); |
|
5 |
foreach (Thesis t in FirstProfessor.Theses) |
|
6 |
{ |
|
7 |
listBox1.Items.Add(t.Name); |
|
8 |
} |
|
9 |
} |
|
В данной программе метод FirstOrDefault находит первый элемент из коллекции Professors и присваивает переменной FirstProfessor.
Далее идет загрузка данных через обращение к методу db.Entry(), в который передается нужный объект. Для подгрузки связанного объекта, который представляет коллекцию, используется метод Collection(). В этот метод передается навигационное свойство в виде строки, по которому надо подгрузить данные.
Если связанные объект не представляет коллекцию, то применяется метод Reference(), в который также передается навигационное свойство.
11
Ленивая загрузка
Еще один способ представляет так называемая «ленивая загрузка» или lazy loading. При таком способе подгрузки при первом обращении к объекту,
если связанные данные не нужны, то они не подгружаются. Однако при первом же обращении к навигационному свойству эти данные автоматически подгружаются из базы данных.
При использовании ленивой загрузки надо учитывать некоторые ограничении при описании классов. Так, классы, использующие ленивую загрузку должны быть публичными, а их свойства должны иметь модификаторы public и virtual. Например, классы Professor и Thesis
могут иметь следующее определение:
Листинг 17.22
1 public class Professor
2 {
3 public int Id { get; set; }
4 public string Name { get; set; }
5 public string Position { get; set; }
6 public string PersonalData { get; set; }
7 public virtual ICollection<Thesis> Theses { get; set; }
8 public Professor()
9 {
10 this.Theses = new List<Thesis>();
11 }
12 }
13 public class Thesis
14 {
15 public int Id { get; set; }
16 public string Name { get; set; }
17 public int? ProfessorId { get; set; }
18 public virtual Professor Professor { get; set; }
19 }
В этом случае нам не потребуется использовать какие-то дополнительные методы, как Include или Load:
Листинг 17.23
1 if (this.Professor.Theses.Count != 0)
12
2 |
{ |
3 |
for (int i = 0; i < this.Professor.Theses.Count; i++) |
4 |
{ |
5 |
listBox1.Items.Add(this.Professor.Theses.ToList()[i].Name); |
6 |
} |
7 |
} |
В данной программе происходит добавление в список listBox1
наименований тем дипломных проектов Professor.Theses.ToList()[i].Name,
закрепленных за преподавателем Professor.
§17.9 Связь один к одному
Строго говоря в Entity Framework нет как таковой связи один-к-одному,
так как ожидается, что обработчик будет использовать связь один-ко-многим.
Но все же нередко возникает потребность в наличие подобной связи между объектами в приложении, и в Entity Framework мы можем настроить данный тип отношений.
Рассмотрим стандартный пример подобных отношений: есть класс пользователя User, который хранит логин, пароль и роль, то есть данные учетных записей. А все данные профиля, такие как имя, номер группы (для студента), должность (для дипломного руководителя) и так далее, выделяются в класс профиля UserProfile.
|
Листинг 17.24 |
|
|
|
|
1 |
using System.Collections.Generic; |
|
2 |
using System.ComponentModel.DataAnnotations; |
|
3 |
using System.ComponentModel.DataAnnotations.Schema; |
|
4 |
… |
|
5 |
public class User |
|
6 |
{ |
|
7 |
public int Id { get; set; } |
|
8 |
public string Login { get; set; } |
|
9 |
public string Password { get; set; } |
|
10 |
public string Role { get; set; } |
|
11 |
public UserProfile Profile { get; set; } |
|
12 |
} |
|
13 |
public class UserProfile |
|
14 |
{ |
|
|
13 |
|
15 |
[Key] |
16 |
[ForeignKey("User")] |
17 |
public int Id { get; set; } |
18 |
public string Name { get; set; } |
19 |
public int NumberGroup { get; set; } |
20 |
public string PersonalDate { get; set; } |
21 |
public User User { get; set; } |
22 |
public Professor Professor { get; set; } |
23 |
} |
В этой связи между классами класс UserProfile является дочерним или подчиненным по отношению к классу User. И чтобы установить связь одни к одному, у подчиненного класса устанавливается свойство идентификатора, которое называется также, как и идентификатор в основном классе. То есть в классе User свойство называется Id, то и в UserProfile
также свойство называется Id. Если бы в классе User свойство называлось бы
UserId, то такое же название должно было быть и в UserProfile.
И в классе UserProfile над этим свойством Id устанавливаются два атрибута: [Key], который показывает, то это первичный ключ, и
[ForeignKey], который показывает, что это также и внешний ключ. Причем
внешний ключ к таблице объектов User.
|
|
Соответственно классы User и UserProfile имеют ссылки друг на |
|
друга. |
|||
|
|
В классе контекста определяются свойства для взаимодействия с |
|
таблицами в базе данных: |
|||
|
|
Листинг 17.25 |
|
|
|
|
|
|
1 |
public class UserContext : DbContext |
|
|
2 |
{ |
|
|
3 |
public UserContext() |
|
|
4 |
{ } |
|
|
5 |
public DbSet<User> Users { get; set; } |
|
|
6 |
public DbSet<UserProfile> UserProfiles { get; set; } |
|
|
7 |
public DbSet<Professor> Professors { get; set; } |
|
|
8 |
public DbSet<Thesis> Theses { get; set; } |
|
|
9 |
} |
|
|
|
14 |
|
Для этих классов контекст данных будет создавать следующую таблицу
UserProfiles:
Листинг 17.26
1 |
CREATE TABLE [dbo].[UserProfiles] ( |
||
2 |
[Id] |
INT |
NOT NULL, |
3 |
[Name] |
NVARCHAR (MAX) NULL, |
|
4 |
[NumberGroup] |
INT |
NOT NULL, |
5 |
[PersonalDate] NVARCHAR (MAX) NULL, |
||
6 |
CONSTRAINT [PK_dbo.UserProfiles] PRIMARY KEY CLUSTERED ([Id] |
||
|
ASC), |
|
|
7 |
CONSTRAINT [FK_dbo.UserProfiles_dbo.Users_Id] FOREIGN KEY |
||
([Id]) REFERENCES [dbo].[Users] ([Id]) |
|||
8 |
); |
|
|
Посмотрим, как работать с моделями с такой связью. Рассмотрим сценарий регистрации пользователей в информационную систему.
Создадим форму для регистрации пользователей:
Рисунок 5
15
Реализация метода обработчика события нажатия на кнопку «Зарегистрироваться»:
|
|
Листинг 17.27 |
|
|
|
|
|
|
if (textBoxUserLogin.Text != "" |
|
|
|
&& textBoxUserPassword.Text != "" |
|
|
|
&& textBoxUserProfileName.Text != "" |
С помощью составного условия проверяется |
|
1 |
&& textBoxUserProfileNumberGroup.Text != "" |
заполненность всех полей. |
|
|
|||
|
&& textBoxUserProfilePersonalDate.Text != "" |
|
|
|
&& comboBoxUserRole.Text != "") |
|
|
|
|
|
|
2 |
{ |
|
|
|
|
||
|
|
|
|
3 |
using (UserContext db = new UserContext()) |
Подключение контекста данных UserContext |
|
|
|
||
|
|
|
|
4 |
{ |
|
|
|
|
||
|
|
|
|
|
User user = new User { Login = |
Создание объекта user класса User и |
|
|
textBoxUserLogin.Text, Password = |
||
|
инициализация его свойств данными, |
||
5 |
textBoxUserPassword.Text, Role = |
||
|
|||
|
введенными через соответствующие textBox |
||
|
comboBoxUserRole.Text }; |
||
|
|
||
|
|
|
|
|
db.Users.Add(user); |
Добавление созданного объекта user в |
|
6 |
коллекцию сущностей Users из контекста |
||
|
|||
|
|
||
|
|
|
16
|
|
данных, отвечающую за хранение сущностей в |
|
|
|
базе данных |
|
|
|
|
|
|
db.SaveChanges(); |
Сохранение изменений результата запроса в базе |
|
7 |
данных |
||
|
|||
|
|
||
|
|
|
|
|
if (comboBoxUserRole.Text == "Студент") |
Если выбранное значение в раскрывающемся |
|
8 |
списке comboBox это "Студент" |
||
|
|||
|
|
||
|
|
|
|
9 |
{ |
|
|
|
|
||
|
|
|
|
|
UserProfile userProfile = new UserProfile { Id = |
|
|
|
user.Id, Name = textBoxUserProfileName.Text, |
Создание объекта userProfile класса |
|
|
NumberGroup = |
UserProfile и инициализация его свойств |
|
10 |
int.Parse(textBoxUserProfileNumberGroup.Text), |
данными, введенными через соответствующие |
|
|
PersonalDate = textBoxUserProfilePersonalDate.Text |
textBox |
|
|
}; |
|
|
|
|
|
|
|
|
Добавление созданного объекта userProfile в |
|
|
db.UserProfiles.Add(userProfile); |
коллекцию сущностей UserProfiles из |
|
11 |
контекста данных, отвечающую за хранение |
||
|
|
||
|
|
сущностей в базе данных |
|
|
|
|
17
|
db.SaveChanges(); |
Сохранение изменений результата запроса в базе |
|
12 |
данных |
||
|
|
||
|
|
|
|
13 |
} |
|
|
|
|
|
|
|
else if (comboBoxUserRole.Text == "Преподаватель") |
Если выбранное значение в раскрывающемся |
|
14 |
списке comboBox это "Преподаватель" |
||
|
|
||
|
|
|
|
15 |
{ |
|
|
|
|
|
|
|
UserProfile userProfile = new UserProfile { Id = |
Создание объекта userProfile класса |
|
|
UserProfile и инициализация его свойств |
||
|
user.Id, Name = textBoxUserProfileName.Text, |
||
|
данными, введенными через соответствующие |
||
16 |
NumberGroup = 0, PersonalDate = |
||
textbox (для преподавателя номер группы будет |
|||
|
textBoxUserProfilePersonalDate.Text }; |
||
|
по умолчанию равен 0) |
||
|
|
||
|
|
|
|
|
|
Добавление созданного объекта userProfile в |
|
|
db.UserProfiles.Add(userProfile); |
коллекцию сущностей UserProfiles из |
|
17 |
контекста данных, отвечающую за хранение |
||
|
|
||
|
|
сущностей в базе данных |
|
|
|
|
|
|
db.SaveChanges(); |
Сохранение изменений результата запроса в базе |
|
18 |
данных |
||
|
|
||
|
|
|
18
|
|
Создание объекта professor класса Professor |
|
Professor professor = new Professor { Id = |
и инициализация его свойств данными, |
|
userProfile.Id, Name = |
введенными через соответствующие textbox |
19 |
textBoxUserProfileName.Text, PersonalData = |
(при регистрации преподавателя создается |
|
textBoxUserProfilePersonalDate.Text, Position = |
последовательно две сущности – для хранения |
|
textBoxUserProfileNumberGroup.Text }; |
профиля преподавателя как пользователя |
|
|
системы и дипломного руководителя) |
|
|
|
|
|
Добавление созданного объекта professor в |
|
db.Professors.Add(professor); |
коллекцию сущностей Professors из контекста |
20 |
данных, отвечающую за хранение сущностей в |
|
|
|
|
|
|
базе данных |
|
|
|
|
db.SaveChanges(); |
Сохранение изменений результата запроса в базе |
21 |
данных |
|
|
|
|
|
|
|
22 |
} |
|
|
|
|
23 |
} |
|
|
|
|
|
this.MainForm.Visible = true; |
Переход в форму авторизации после успешной |
24 |
регистрации |
|
|
|
|
|
|
|
25 |
this.Visible = false; |
Скрытие формы регистрации |
|
|
|
26 |
} |
|
|
|
|
|
19 |
|
Обратите внимание, что при создании двух объектов, связанных между собой отношением один ко многим,
необходимо инициализировать атрибут Id подчиненного объекта (в нашей случае подчиненным является userProfile
в паре user-userProfile и professor в паре userProfile-professor) значением атрибута Id родительского объекта. Например, в строке 10 и 16 при создании объекта userProfile свойству Id было присвоено значение user.Id,
то есть Id родительского объекта user, который был предварительно создан.
Рассмотрим пример редактирования данных. Добавим возможность редактирования логина, пароля и информации
«О себе» после входа в личную страницу пользователя в информационной системе. Создадим следующую форму для редактирования данных учетной записи пользователя:
Рисунок 6
Реализация метода обработчика события нажатия на кнопку подтверждения изменений « »:
20