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(), в который также передается навигационное свойство.
Ленивая загрузка
Еще один способ представляет так называемая «ленивая загрузка» или lazy loading. При таком способе подгрузки при первом обращении к объекту,
если связанные данные не нужны, то они не подгружаются. Однако при первом же обращении к навигационному свойству эти данные автоматически подгружаются из базы данных.
При использовании ленивой загрузки надо учитывать некоторые ограничении при описании классов. Так, классы, использующие ленивую загрузку должны быть публичными, а их свойства должны иметь модификаторы public и virtual. Например, классы Professor и Thesis
могут иметь следующее определение:
Листинг 17.22
1public class Professor
2{
3public int Id { get; set; }
4public string Name { get; set; }
5public string Position { get; set; }
6
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) |
|
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.
7
§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 |
{ |
|
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 |
} |
|
|
8 |
|
В этой связи между классами класс 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 |
} |
|
|
|
|
|
Для этих классов контекст данных будет создавать следующую таблицу |
|||
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, |
|
||
|
|
|
|
9 |
|
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 |
); |
Посмотрим, как работать с моделями с такой связью. Рассмотрим сценарий регистрации пользователей в информационную систему.
Создадим форму для регистрации пользователей:
Рисунок 1
10