Добавил:
Преподаватель Колледжа информационных технологий Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Лекции / Глава 17.2 Entity Framework

.pdf
Скачиваний:
44
Добавлен:
08.05.2022
Размер:
1.06 Mб
Скачать

ГЛАВА 17.2 ENTITY FRAMEWORK

 

Оглавление

 

§17.8 Навигационные свойства и загрузка данных .............................................

1

§17.9 Связь один к одному .....................................................................................

8

§17.10 Связь один ко многим...............................................................................

20

§17.11 Миграции ...................................................................................................

26

§17.8 Навигационные свойства и загрузка данных

В предыдущей теме в качестве модели был использован класс User,

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

а несколько таблиц, которые связаны между собой различными связями.

Рассмотрим создание модели данных для информационной системы по поиску дипломных руководителей для студентов отделений среднего профессионального образования. Допустим, для каждого дипломного руководителя может быть определен набор примерных тем для дипломного проекта, которые он предлагает студентам. И, наоборот, одна примерная тема дипломного проекта может быть предложена только одним дипломным руководителем. То есть в данном случае у нас связь один-ко-многим (one-to- many).

Например, у нас определен следующий класс Professor:

Листинг 17.16

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 ICollection<Thesis> Theses { get; set; }

8

public Professor()

9

{

10

this.Theses = new List<Thesis>();

11

}

 

1

12 }

Здесь Name – это ФИО дипломного руководителя, Position – его

должность, PersonalData – информация об его профессиональной деятельности, Theses – внешний ключ, представляющий собой обобщенную коллекцию ICollection<Thesis> для хранения тем дипломных проектов

Thesis, предлагаемых им.

Интерфейс обобщенной коллекции ICollection<T> представляет ряд общих свойств и методов для всех обобщенных коллекций (например, методы

CopyTo, Add, Remove, Contains, свойство Count).

 

 

А класс Thesis, описывающий дипломный проект, мог бы выглядеть

следующим образом:

 

 

Листинг 17.17

 

 

 

 

 

1

public class Thesis

 

 

2

{

 

 

3

public int Id { get; set; }

 

 

4

public string Name { get; set; }

 

 

5

public int? ProfessorId { get; set; }

 

 

6

public Professor Professor { get; set; }

 

 

7

}

 

Кроме обычного свойства Name (наименование темы дипломного проекта), здесь также определен внешний ключ. Внешний ключ состоит из обычного свойства и навигационного.

Свойство

public Professor Professor { get; set; }

в классе Thesis называется навигационным свойством - при получении данных об дипломном руководителе оно будет автоматически получать данные из базы данных.

Аналогично в классе Professor также имеется навигационное свойство

Theses, через которое мы можем получать темы дипломных проектов,

предлагаемые данным преподавателем.

2

Вторая часть внешнего ключа - свойство ProfessorId. Чтобы в связке

с навигационным свойством образовать внешний ключ оно должно принимать

одно из следующих вариантов имени:

1.Имя_навигационного_свойства + Имя ключа из связанной таблицы

- в нашем случае имя навигационного свойства Professor, а ключа из модели Professor Id, поэтому в нашем случае нам надо обозвать свойство ProfessorId, что собственно и было сделано в вышеприведенном коде.

2.Имя_класса_связанной_таблицы + Имя ключа из связанной таблицы - в нашем случае класс Professor, а ключа из модели

Professor - Id, поэтому опять же в этом случае получается

ProfessorId.

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

таблица Theses будет иметь следующее определение:

 

 

 

Листинг 17.18

 

 

 

1

CREATE TABLE [dbo].[Theses] (

 

2

[Id]

INT

IDENTITY (1, 1) NOT NULL,

 

3

[Name]

NVARCHAR (MAX) NULL,

 

4

[ProfessorId] INT

NULL,

 

5

CONSTRAINT [PK_dbo.Theses] PRIMARY KEY CLUSTERED ([Id] ASC),

 

6

CONSTRAINT [FK_dbo.Theses_dbo.Professors_ProfessorId] FOREIGN

 

KEY ([ProfessorId]) REFERENCES [dbo].[Professors] ([Id])

 

7

);

 

 

 

При определении внешнего ключа нужно иметь в виду следующее. Если тип обычного свойства во внешнем ключе определяется как int?, то есть допускает значения null, то при создании базы данных соответствующее поле так будет принимать значения NULL:

[ProfessorId] INT NULL.

Однако если мы изменим в классе Thesis тип ProfessorId на просто

int:

public int ProfessorId { get; set; }, 3

то в этом случае соответствующее поле имело бы ограничение NOT NULL, а

внешний ключ определял бы каскадное удаление:

 

 

 

Листинг 17.19

 

 

 

1

CREATE TABLE [dbo].[Theses] (

 

2

[Id]

INT

IDENTITY (1, 1) NOT NULL,

 

3

[Name]

NVARCHAR (MAX) NULL,

 

4

[ProfessorId] INT

NOT NULL,

 

5

CONSTRAINT [PK_dbo.Theses] PRIMARY KEY CLUSTERED ([Id] ASC),

 

 

CONSTRAINT [FK_dbo.Theses_dbo.Professors_ProfessorId] FOREIGN

 

6

KEY ([ProfessorId]) REFERENCES [dbo].[Professors] ([Id]) ON

 

 

DELETE CASCADE

 

 

7

);

 

 

 

4

Способы загрузки и получения связанных данных

В Entity Framework есть три способа загрузки данных:

1.eager loading ("жадная загрузка")

2.explicit loading ("явная загрузка")

3.lazy loading ("ленивая загрузка")

Жадная загрузка

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

получим темы дипломных работ, предлагаемые выбранным в списке руководителем:

Листинг 17.20

1

using (UserContext db = new UserContext())

2

{

3

var professors =

db.Professors.Include("Theses").ToList();

 

4

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

{

 

5

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