Создание модели данных entity framework для приложения asp net mvc
Для взаимодействия с MS SQL Server через Entity Framework необходим пакет Microsoft.EntityFrameworkCore.SqlServer . По умолчанию он отсутствует в проекте, поэтому его надо добавить, например, через пакетный менеджер Nuget:
Далее создадим в проекте папку Models и в нее добавим новый класс, который назовем User :
Эта модель представляет те объекты, которые будут храниться в базе данных.
Чтобы взаимодействовать с базой данных через Entity Framework нам нужен контекст данных - класс, унаследованный от класса Microsoft.EntityFrameworkCore.DbContext . Поэтому добавим в папку Models новый класс, который назовем ApplicationContext :
Свойство DbSet представляет собой коллекцию объектов, которая сопоставляется с определенной таблицей в базе данных. При этом по умолчанию название свойства должно соответствовать множественному числу названию модели в соответствии с правилами английского языка. То есть User - название класса модели представляет единственное число, а Users - множественное число.
Через параметр options в конструктор контекста данных будут передаваться настройки контекста.
В конструкторе с помощью вызова Database.EnsureCreated() по определению моделей будет создаваться база данных (если она отсутствует).
Чтобы подключаться к базе данных, нам надо задать параметры подключения. Для этого изменим файл appsettings.json , добавив в него определение строки подключения:
В данном случае мы будем использовать упрощенный движок базы данных LocalDB, который представляет легковесную версию SQL Server Express, предназначенную специально для разработки приложений.
И последним шагом в настройке проекта является изменение файла Program.cs :
Добавление контекста данных в виде сервиса позволит затем получать его в конструкторе контроллера через механизм внедрения зависимостей.
Creating Database using Migrations
Now, we will create the database using the EF Core Migrations feature. Migrations lets us create a database that matches our data model and update the database schema when our data model changes.
First, we will add an initial Migration.
Open Tools -> NuGet Package Manager > Package Manager Console(PMC) and run the following command in the PMC:
The Add-Migration command generates code to create the initial database schema which is based on the model specified in the TvShowsContext class. The Initial argument is the migration name and any name can be used.
After running the command, a migration file is created under the Migrations folder:
As the next step, run the following command in the PMC:
The Update-Database command runs the Up method in the Migrations/_InitialCreate.cs file, which creates the database.
Now, we will check the database created. Open View -> Sql Server Object Explorer.
You will see the newly created database under the following path:
As you see, the TvShows table and the migration history table are created automatically. Then a record is inserted to the migration history table to show the executed migrations on the database.
Получение контекста данных в контроллере
Поскольку выше в приложении контекст данных добавляется в виде сервиса, то в конструкторе контроллера мы можем получить переданный контекст данных. Например, пусть для хранения контроллеров в проекте определена папка Controllers , в которой есть класс контроллера - HomeController :
Для взаимодействия с базой данных в контроллере определяется переменная контекст данных ApplicationContext db . Причем поскольку в приложении контекст данных добавляется в виде сервиса, то в конструкторе контроллера мы можем получить переданный контекст данных.
В этом уроке вы увидите примеры атрибутов, которвые вы можете добавить в классы моделей для управления форматированием, валидацией и маппингом базы данных. Затем вы создадите полноценную модель данных School путём добавления атрибутов в уже созданные классы и созданием новых классов для оставшихся в модели типов сущностей.
Атрибут DisplayFormat
Для дат записи студентов, все страницы сейчас отображают время вместе с датой, тогда как нам нужна только дата. С помощью аннотаций можно указать в одном месте настройку, которая будет распространяться на всё. Для этого добавим атрибут к свойству EnrollmentDate в классе Student.
Строка форматирования указывает, что необходимо отображать только короткую форму даты этого свойства. Настройка ApplyFormatInEditMode указывает, что данное форматирование должно применяться также для значений, отображающихся в текстовых строках, предназначенных для редактирования. (допустим, нам не нужно отображать в некоторых полях, например, в которых содержатся значения, связанные с валютой, знак валюты)
Убедитесь, что на страницах About и Student время в датах больше не показывается.
Атрибут MaxLength
Атрибутами можно определить правила валидации, допустим, в том случае, если необходимо ограничить введённое пользователем значение 50-ю символами. Чтобы воспользоваться этой возможностью, добавьте атрибут Range в свойствам LastName и FirstMidName:
Откройте страницу Create и попробуйте ввести два имени из более чем 50-ти символов, затем нажмите Create чтобы убедиться что валидация работает.
Указывать максимальное значение для вводимых элементов – одна из хороших практик. В ином случае, если используется подход Code First, при создании базы данных имена соответствующих столбцов будут иметь максимально допустимую длину.
Атрибут Column
Атрибутами можно контролировать маппинг классов на базу данных. Допустим, вы использовали имя FirstMidName для поля «Имя» потому, что это поле могло также содержать отчество. Но вы хотите, чтобы в базе данных соответствующий столбец назывался FirstName, потому что пользователи пользуются в своих запросах именно этим наименованием. Для этого можно воспользоваться атрибутом Column.
Атрибут Column указывает, что при создании базы данных, столбец таблицы Student, который маппится на свойство FirstMidName, будет называться FirstName.
Добавьте этот атрибут к свойству FirstMidName:
Откройте страницу Student Index, на которой ничего не изменилось (необходимо не просто запустить сайт и посмотреть на домашнюю страницу, необходимо перейти на страницу Student Index чтобы инициировать обращение к базе данных, что инициирует также ее удаление и пересоздание). Однако, если вы откроете базу данных ServerExplorer вы увидите, что в таблице Student столбец называется FirstName.
В окне Propertiesможно заметить, что длина ограничена 50-тью символами благодаря использованию атрибута MaxLength.
Создание сущности Instructor
Создайте Models\Instructor.csсо следующим кодом:
Обратите внимание, что некоторые из свойств аналогичны свойствам сущности Student. В Implementing Inheritance мы используем наследование для решения проблемы избыточности.
Атрибуты Required и Display
Атрибуты свойства LastName указывают на то, что это необходимое для заполнения значение, заголовок для текстового поля должен быть равен “Last Name” (вместо названия свойства “LastName”) и длина введённого значения должна быть не более 50-ти символов..
Вычисляемое свойство FullName
FullName является вычисляемым свойством, которое содержит значение, вычисляемое путем соединения двух других свойств. Поэтому оно имеет только аксессор, в базе данных же не создаётся соответствующего столбца.
Navigation Properties Courses и OfficeAssignment
Свойства Courses и OfficeAssignment — navigation properties. Как было упомянуто ранее, они обычно помечаются модификатором virtual чтобы использовать lazy loading. Кроме этого, если данное свойство может содержать несколько сущностей, его тип должен быть Icollection.
Поскольку преподаватель может вести несколько курсов, переменная Courses определена как коллекция сущностей Course. С другой стороны, преподаватель может иметь только один офис, поэтому OfficeAsignment определн как единственная сущность типа OfficeAssignment (которая может быть равна null, если у преподавателя нет офиса).
Создание сущности OfficeAssignment
Создайте Models\OfficeAssignment.cs со следующим содержанием:
Атрибут Key
Между сущностями Instructor и OfficeAssignment существует связь один-к-нулю-или-единице. Назначенный офис существует только в сочетании с назначенным преподавателем, поэтому его первичный ключ также является внешним ключом на сущность Instructor. Entity Framework, однако, не может автоматически распознать InstructorID как первичный ключ поскольку имя данного свойства не соответствует конвенциям именования первичных ключей, не равно ID и classnameID. Поэтому атрибутом Key указывается, что данный атрибут является первичным ключом.
Атрибут Key можно использовать также для той ситуации, когда у вас уже есть первичный ключ, но вы хотите назвать свойство иначе чем classnameID или ID.
Instructor Navigation Property
Сущность Instructor nullable OfficeAssignment navigation property (преподаватель может не иметь офиса), а сущность OfficeAssignment имеет не-nullable Instructor navigation property (офис не может существовать без преподавателя). Когда Instructor имеет ассоциированую с ним сущность OfficeAsignment, оба имеют друг на друга ссылки в своих navigation property.
-
или Visual Web Developer Express 2010 SP1 (если вы используете для установки одну из этих ссылок, следующий софт установится автоматически)
The Contoso University
Приложение, которые вы разработаете, является простым вебсайтом университета.
Пользователи смогут просматривать и обновлять данные о студентах, курсах и преподавателях. Несколько скриншотов того, что получится, ниже.
UI близок по стилю к тому, что генерируется шаблонами по умолчанию, поэтому акцент будет на вопросах использования Entity Framework.
Подходы к разработке с Entity Framework
Исхоя из диаграммы, имеется три подхода к работе с данными в Entity Framework: Database First, Model First, и Code First.
Database First
В случае уже имеющейся базы данных Entity Framework может автоматически создать модель данных, состоящую из классов и свойств, соответствующих объектам базы даных (таким, как таблицы и столбцы). Информация о структуре базы (store schema), модель данных (conceptual model) и маппинг их друг на друга содержится в XML в файле .edmx. Visual Studio предоставляет графический дизайнер Entity Framework, с помощью которого можно просматривать и редактировать .edmx. Части Getting Started With the Entity Framework и Continuing With the Entity Framework в материалах о Web Forms используют подход Database First.
Model First
Если базы нет, вы можете начать с создания модели данных, используя дизайнер Entity Framework Visual Studio. После окончания работ над моделью дизайнер сгенерирует DDL (data definition language)-код для создания базы. В этом подходе для хранения информации о модели и маппингах также используется .edmx. What's New in the Entity Framework 4 включает небольшой пример разработки с использованием данного подхода.
Code First
Вне зависимости от наличия базы вы можете вручную написать код классов и свойств, соответствующих сущностям в базе и использовать этот код с Entity Framework без использования файла .edmx. Именно поэтому можно порой увидеть, как этот подход называют code only, хотя официальное наименование Code First. Маппинг между store schema и conceptual model в represented by your code is handled by convention and by a special mapping API. Если базы ещё нет, Entity Framework может создать, удалить или пересоздать её в случае изменений в модели.
API доступа к данным, разработанное для Code First, основано на классе DbContext . API может быть использован также и в процессе разработки с подходами Database First и Model First. Для дополнительной информации смотрите When is Code First not code first? В блоге команды разработки Entity Framework.
POCO (Plain Old CLR Objects)
По умолчанию для подходов Database First и Model First классы модели данных наследуются от EntityObject, который и предоставляет функциональность Entity Framework. Это значит, что эти классы не являются persistence ignorant и, таким образом, не полностью соответствуют одном из условий domain-driven design. Все подходы к разработке с Entity Framework также могут работать с POCO (plain old CLR objects), что, в целом, значит, что они являются persistence-ignorant из-за отсутствия наследования EntityObject .
Создание веб-приложения MVC
Настройка стилей
Несколько небольших поправок изменят меню сайта, расположение элементов и домашнюю страницу.
Для настройки меню Contoso University, в Views\Shared\_Layout.cshtml замените текст в h 1 и ссылки в меню, как в примере:
В Views\Home\Index.cshtml удалите всё в теге h2 .
В Controllers\HomeController.cs замените "Welcome to ASP.NET MVC!" на "Welcome to Contoso University!"
В Content\Site.css для смещения меню влево совершите следующие изменения:
Создание модели данных
Далее создадим первые классы-сущности для Contoso University. Мы начнём со следующих трёх сущностей:
Установлена связь один-ко-многим между сущностями Student и Enrollment , и связь один-ко-многим между Course и Enrollment . Другими словами, студент может посещать любое количество курсов, и курс может иметь любое количество студентов, посещающих его.
В дальнейшем вы создадите классы для каждой из этих сущностей.
Note: компиляция проекта без созданных классов для этих сущностей вызовет ошибки компиляторов.
Сущность Student
В папке Models создайте Student.cs и замените сгенерированный код на:
Свойство StudentID будет первичным ключом соответствующей таблицы. По умолчанию, Entity Framework воспринимает свойство с ID или classname ID как первичный ключ.
Свойство Enrollments — navigation property. Navigation properties содержат другие сущности, относящиеся к текущей. В данном случае свойство Enrollments содержит в себе все сущности Enrollment , ассоциированные с текущей сущностью Student . Другими словами, если некая запись Student в базе данных имеет связь с двумя записями Enrollment (записей, содержащих значения первичных ключей для студента в поле внешнего ключа StudentID ), свойство этой записи Enrollments будет содержать две сущности Enrollment .
Navigation properties обычно помечаются модификатором virtual дабы использовать возможность Entity Framework, называемую lazy loading. (суть Lazy loading будет объяснена позже, в Reading Related Data) Если navigation property может содержать несколько сущностей (в связях многие-ко-многим и один-ко-многим), его тип должен быть ICollection .
Сущность Enrollment
В папке Models создайте Enrollment.cs со следующим содержанием:
Знак вопроса после указания типа decimal указывает на то, что свойство Grade nullable. Оценка, поставленная в null отличная от нулевой оценки— null обозначает то, что оценка еще не выставлена, тогда как 0 – уже значение.
Свойство StudentID является внешним ключом, и соответствующее navigation property Student . Сущность Enrollment ассоциирована с одной сущностью Student , поэтому свойство может содержать только одну сущность указанного типа (в отличие Student . Enrollments ).
Свойство CourseID является внешним ключом, и соответствующее navigation property Course . Сущность Enrollment ассоциирована с одной сущностью Course .
Сущность Course
В папке Models создайтеCourse.cs со следующим содержанием:
Свойство Enrollments — navigation property. Сущность Course может ассоциироваться с бесконечным множеством сущностей Enrollment .
Создание Database Context
Главный класс, координирующий функциональность Entity Framework для текущей модели данных называется database context. Данный класс наследуется от System . Data . Entity . DbContext . В коде вы определяете, какие сущности включить в модель данных, и также можете определять поведение самого Entity Framework. В нашем коде этот класс имеет название SchoolContext .
Создайте папку DAL и в ней новый класс SchoolContext.cs:
Код создаёт свойство DbSet для каждого множества сущностей. В терминологии Entity Framework множество сущностей (entity set) относится к таблице базы данных, и сущность относится к записи в таблице.
Содержимое метода OnModelCreating защищает имена таблиц от плюрализации, и, если вы этого не делаете, то получаете такие имена таблиц, как Students , Courses , Enrollments . В ином случае имена таблиц будут Student , Course , Enrollment . Разработчики спорят на тему того, нужно ли плюрализовывать имена таблиц или нет. Мы используем одиночную форму, но важен тот момент, что вы можете выбрать, включать эту строчку в код или нет.
(Этот класс находится в namespace Models потому, что в некоторых ситуациях подход Code First подразумевает нахождение классов сущностей и контекста в одном и том же namespace.)
Определение Connection String
Вам не нужно определять connection string. Если вы не определили эту строку, Entity Framework автоматически создаст базу данных SQL Server Express. Мы, однако, будем работать с SQL Server Compact, и вам необходимо будет создать строку подключения с указанием на это.
Откройте Web.config и добавьте новую строку подключения в коллекцию connectionStrings . (Убедитесь, что вы обновляете Web.config в корне проекта, так как есть еще один Web.config в папке Views, который трогать не надо.)
По умолчанию Entity Framework ищет строку подключения, названную также как object context class. Строка подключения, которую вы добавили, определяет базу данных School.sdf, находящуюся в папке App_data и SQL Server Compact.
Инициализация базы данных с тестовыми данными
Entity Framework может автоматически создать базу данных при запуске приложения. Вы можете указать, что это должно выплоняться при каждом запуске приложения или только тогда, когда модель рассинхронизирована с существующей базой. Вы можете также написать класс с методом, который Entity Framework будет автоматически вызывать перед созданием базы для использования её с тестовыми данными. Мы укажем, что база должна удаляться и пересоздаваться при изменении модели.
В папке DAL создайте новый класс SchoolInitializer.cs с кодом, с помощью которого база будет создана при необходимости и заполнена тестовыми данными.
Метод Seed принимает объект контекста базы как входящий параметр и использует его для добавления новых сущностей в базу. Для каждого типа сущности код создает коллекцию новых сущностей, добавляя их в соответствующее свойство DbSet, и потом сохраняет изменения в базу. Нет необходимости в вызове SaveChanges после каждой группы сущностей, как сделано у нас, но это помогает определить проблему в случае возникновения исключений.
Измените Global.asax.cs для того, чтобы наш код вызывался при каждом запуске приложения:
- Добавьте using :
- В методе Application_Start вызовите метод Entity Framework, который запускает код инициализации базы:
Note при развертывании приложения на продакшн-сервер вы должны удалить весь код, который инициализирует базу тестовыми данными.
Далее вы создадите веб-страницу для отображения данных, и процесс запроса данных автоматически инициирует создание базы. Вы начнете с нового контроллера, но перед этим, соберите проект для того, чтобы модель и контекстные классы стали доступны для MVC controller scaffolding.
Создание контроллера Student
Для создание контроллера Student , щелкните на папке Controllers в Solution Explorer, нажмите Add, Controller. В Add Controller совершите следующие действия и изменения и нажмите Add:
- Controller name: StudentController.
- Template: Controller with read/write actions and views, using Entity Framework. (по умолчанию.)
- Model class: Student (ContosoUniversity.Models). (если этого нет, пересоберите проект)
- Data context class: SchoolContext (ContosoUniversity.Models).
- Views: Razor (CSHTML). (по умолчанию)
Откройте Controllers\StudentController.cs, вы увидите созданную переменную, инициализирующую объект контекста базы данных:
Действие Index собирает список студентов из свойства Students из экземпляра контекста базы данных:
Автоматическое scaffolding было создано для множества Student . Для настройки заголовков и последовательности колонок откройте Views\Student\Index.cshtml и замените код на:
Запустите сайт, нажмите на вкладку Students.
Закройте браузер. В Solution Explorer выберите проект ContosoUniversity. Нажмите Show all Files, Refresh и затем разверните папку App_Data.
Два раза щелкните на School.sdf для открытия Server Explorer, и Tables.
Note если у вас возникает ошибка после того, как вы два раза щелкаете на School.sdf, убедитесь, что вы установили Visual Studio 2010 SP1 Tools for SQL Server Compact 4.0. Если все установлено, перезапустите Visual Studio.
Для каждой таблицы есть свое множество сущностей + одна дополнительная таблица. EdmMetadata используется для определения Entity Framework, когда случилась разсинхронизация модели и базы.
Щелкните на одной из таблиц и Show Table Data чтобы увидеть загруженные классом SchoolInitializer данные.
Закройте подключение, иначе может возникнуть проблема с запуском приложения.
Соглашения
Количество кода, нужное для создания Entity Framework базы, минимально из-за использования (conventions) Entity Framework. Некоторые из них уже были упомянуты:
- Форма множественного числа имен классов сущностей используется в качестве имен таблиц.
- Имена свойств сущностей используется в качестве имен столбцов.
- Свойства сущностей с именами ID или classname ID распознаются как первичные ключи.
- Entity Framework подключается к базе, отыскав строку подключения с таким же именем, как и класс контекста (в данном случае SchoolContext ).
Вы создали простое приложение с использованием Entity Framework и SQL Server Compact для хранения и отображения данных. Далее мы научимся совершать простые CRUD (create, read, update, delete) операции.
В Models\Course.cs замените ранее созданный код на:
Атрибут DatabaseGenerated
Атрибут DatabaseGenerated с параметром None, указанный для свойства CourseID, определяет то, что значение первичного ключа задаётся пользователем, а не генерируется базой данных.
По умолчанию Entity Framework предполагает автогенерацию первичных ключей базой данных, что и необходимо в большинстве ситуаций. Однако для сущности Course используются численные заданные пользователем значения, такие как 1000 для одного факультета, 2000 для другого и так далее.
Внешний ключ и Navigation Properties
Свойства-внешние ключи и navigation properties в сущности Course отражают следующие связи:
Курс ассоциирован с одним факультетом, таким образом, имеется внешний ключ DepartmentID и Department navigation property:
public int DepartmentID < get; set; >
public virtual Department Department
Курс может посещать неограниченное количество студентов, поэтому имеется Enrollments navigation property:
public virtual ICollection Enrollments
Курс может вестись различными преподавателями, поэтому имеется Instructors navigation property:
public virtual ICollection Instructors
Создание сущности Department
Создайте Models\Department.cs со следующим кодом:
Атрибут Column
Ранее атрибут Column мы использовали для изменения маппинга имени столбца. В коде для сущности Department этот атрибут используется для изменения маппинга типа данных SQL, то есть столбец будет определён в базе данных с типом данных SQL Server:
Обычно это не нужно, потому что Entity Framework автоматически подбирает наиболее подходящий тип данных исходя из типа CLR, который определён для свойства. Допустим, CLR тип decimal станет SQL Server типом decimal. Но в данном случае вы точно знаете, что свойство будет содержать цифры, связанные с валютой, и тип money будет наиболее подходящим для этого свойства.
Внешний ключ и Navigation Properties
Внешними ключами и navigation properties отражены следующие связи:
Факультет может как содержать, так и не содержать администратора, и администратор всегда = преподаватель. Поэтому свойство InstructorID определено как внешний ключ для сущности Instructor, и знак вопроса после типа int указывает на то, что свойство может быть nullable. Navigation property Administrator содержит сущность Instructor:
public int? InstructorID
public virtual Instructor Administrator < get; set; >
Факультет может иметь множество курсов, поэтому присутствует Courses navigation property:
public virtual ICollection Courses
Note Конвенциями определено, что Entity Framework каскадно удаляет non-nullable внешние ключи и в случаях связи многие-ко-многим. Это может привести к итеративному каскадному удалению, вызвав исключение при запуске кода. Допустим, если не определить Department.InstructorID как nullable, вы получите следующее исключение при: "The referential relationship will result in a cyclical reference that's not allowed."
Изменения, связанные с сущностью Student
В Models\Student.cs замените код на:
Изменения, касающиеся сущности Enrollment
В Models\Enrollment.cs замените код на:
Внешние ключи и Navigation Properties
Внешние ключи и navigation properties отражают следующие связи:
Каждой сущности записи на курс соответствует один курс, поэтому присутствует внешний ключ CourseID и Course navigation property:
public int CourseID
public virtual Course Course < get; set; >
Каждой сущности записи на курс соответствует один студент, поэтому присутствует внешний ключ StudentID и Student navigation property:
public int StudentID
public virtual Student Student
Связи многие-ко-многим
Сущности Student и Course связаны друг с другом связью многие-ко-многим, и сущность Enrollment соответствует and the Enrollment entity corresponds to a many-to-many join table with payload in the database. Это значит, что таблица Enrollment содержит дополнительные данные помимо внешних ключей для объединённых таблиц (в нашем случае первичный ключ и свойство Grade).
На изображении ниже представлены связи в виде диаграммы сущностей, сгенерированной Entity Framework designer.
Линия каждой связи имеет 1 на одном конце и * на другом, обозначая связь один-ко-многим.
Если таблица Enrollment не содержит информацию об оценках, необходимо иметь только два внешних ключа CourseID и StudentID. In that case, it would correspond to a many-to-many join table without payload (or a pure join table) in the database, and you wouldn't have to create a model class for it at all. Сущности Instructor и Course связаны подобной связью многие-ко-многим, и, как вы видите, между ними нет класса сущности:
Хотя объединённая таблица необходима:
Entity Framework автоматически создаёт таблицу CourseInstructor, доступ к которой осуществляется косвенно, а именно через Instructor.Courses и Course.Instructors navigation properties.
Атрибут DisplayFormat
Атрибут DisplayFormat для свойства Grade определяет форматирование для элемента:
- Оценка выводится в формате "3.5" или "4.0".
- Такое же форматирование оценка имеет и в режиме.
- Если оценки нет, выводится надпись "No grade".
Связи на диаграмме сущностей
Диаграмма ниже демонстрирует систему связей для модели School.
Кроме связей многие-ко-многим (*-*) и один-ко-многим (1-*), можно также увидеть связь один-к-нулю-или-одному (1-0..1) между сущностями Instructor и OfficeAssignment и нуль-к-одному-или-ко-многим (0..1 — *) Department и Instructor.
Настройка контекста базы данных
Дальше мы добавим новые сущности в класс SchoolContext и настроим мапинг. В некоторых случаях необходимо будет использовать методы вместо атрибутов из-за того, что для некоторой функциональности атрибутов просто не сущнствует. В других случаях можно выбирать, использовать методы или атрибуты (некоторые люди предпочитают не использовать атрибуты).
Замените код в DAL\SchoolContext.cs на:
В методе OnModelCreating определяются следующие связи:
Один-к-нулю-или-одному между Instructor и OfficeAssignment:
modelBuilder.Entity().HasOptional(p => p.OfficeAssignment).WithRequired(p => p.Instructor);
Многие-ко-многим между Instructor и Course. Код определяет таблицу и столбцы для объединённой таблицы. Code First может настроить связь многие-ко-многим и без кода, но если вы его не вызовете, то для столбцов будут взяты стандартные имена, такие как InstructorInstructorID для InstructorID.
modelBuilder.Entity()
.HasMany(c => c.Instructors).WithMany(i => i.Courses)
.Map(t => t.MapLeftKey("CourseID")
.MapRightKey("InstructorID")
.ToTable("CourseInstructor"));
Один-к-нулю-или-одному между Department и Instructor, с помощью Department.Administrator navigation property:
Заполнение базы данных тестовыми данными
Перед этим вы создавали DAL\SchoolInitializer.cs для заполнения базы тестовыми данными. Теперь замените старый код на новый, который учитывает присутствие новых сущностей.
Обратите внимание на обработку сущности Course, которая связана связью многие-ко-многим с сущностью Instructor:
При создании объекта Course как пустая коллекция (Instructors = new List()), что делает возможным добавление сущностей Instructor, связанных с Course, с помощью метода Instructor.Add(). Если вы не создали пустой List, у вас не получится добавлять подобные отношения, потому что свойство Instructors будет равно null и не будет иметь метода Add.
Note Не забывайте перед развертыванием проекта на production-сервер удалять весь код инициализации базы.
Удаление и пересоздание базы данных
Запустите проект и выберите страницу Student Index.
Если страница не открывается или вы получаете ошибку о том, что файл School.sdf уже используется (изображение ниже), необходимо еще раз открыть Server Explorer и закрыть подключение к базе и затем попробовать снова открыть страницу.
После этого откройте базу в Server Explorer и посмотрите в Tables новые таблицы.
Кроме EdmMetadata обратите внимание на таблицу, для которой вы не создавали класса CourseInstructor. Это объединённая из Instructor и Course таблица.
Щелкните на таблице CourseInstructor и нажмите Show Table Data чтобы убедиться в наличии данных, добавленных ранее Course.Instructors navigation property.
Теперь у вас есть сложная модель данных и соответствующая база данных. Дальше мы научимся разным способам обращения к данным.
I will demonstrate how to
For the application development, I will use
For a better understanding of this post, I suggest you read the below post first:
If you are ready, let’s get started.
Adding Data Annotations to the Model
In this section, we will add data annotations to our model for validation and display purposes. Data Annotations provides a built-in set of validation attributes that you apply declaratively to any class or property. It also contains formatting attributes that help with formatting and don’t provide any validation.
Change the TvShow.cs as follows:
If we run the application and try to create a new record as below, we can see the effect of these data annotations:
The errors are enforced both client-side (using JavaScript and jQuery) and server-side (in case a user has JavaScript disabled).
For server-side validation, the second Create method (POST version) calls ModelState.IsValid to check whether the movie has any validation errors.
Creating the First Record
Now, run the app and click Tv Shows App and then click Create New link.
For now, Genre drop-downlist is empty. We will edit Create.cshtml to load the list from the Genre enum as below:
After saving the Create.cshtml, if you refresh the browser the change will be reflected and Genre drop-downlist is loaded. Now we can create the first record. (You should do the same change in the Edit.cshtml too)
After clicking the Create button, the new record is shown as below in the Index page:
Besides we can check this from the table in the Sql Server:
Now we will show Imdb Url as a link. Change Views/TvShows/Index.cshtml as follows:
If we run the app again, Index page is shown as below:
You can do the same change for the Details page (Views/TvShows/Detail.cshtml) as well.
Adding a New Field
We will now add Image Url field to our application and show the poster of the TV Show in the records.
First, add this property to the TvShow model (Models/TvShow.cs) as follows:
Then build the application.
Next, we will use Code First Migrations to add this field to the db table. Run the following commands in the PMC:
We can see from Sql Server Object Explorer that the ImageUrl is added to the table as below:
Now, we will update the [Bind] attribute for the Create and Edit POST methods in the TvShowsController.cs:
Next, we need to change the Index, Create, Edit, Delete views to show this new field.
Add the following part shown in the red box to the Edit.cshtml.
And change the Index.cshtml as below:
Go to the Edit page for the record we created and enter the following image url in the Poster field:
You should copy the image file under wwwroot/images directory.
After pressing the Save button, our record is shown as below in the Index page:
You should change the Create, Details and Delete views to show the new field as well.
Lastly, let’s change the display format of the Rating field so that it only shows one digit after the decimal separator. Add the following line to the TvShow.cs and build the application.
Now our application is ready to enter our favourite TV shows. I entered mine, you can enter yours :)
You can access the full project from here.
I hope you enjoyed reading this post and found it helpful and easy to understand. Please let me know if you have any comments and/or corrections.
And if you liked this post, please clap your hands 👏👏👏
If you want, you can follow this publication and be informed when a new post arrives.
Получение контекста данных в контроллере
Поскольку выше в приложении контекст данных добавляется в виде сервиса, то в конструкторе контроллера мы можем получить переданный контекст данных. Например, пусть для хранения контроллеров в проекте определена папка Controllers , в которой есть класс контроллера - HomeController :
Для взаимодействия с базой данных в контроллере определяется переменная контекст данных ApplicationContext db . Причем поскольку в приложении контекст данных добавляется в виде сервиса, то в конструкторе контроллера мы можем получить переданный контекст данных.
В этом уроке вы увидите примеры атрибутов, которвые вы можете добавить в классы моделей для управления форматированием, валидацией и маппингом базы данных. Затем вы создадите полноценную модель данных School путём добавления атрибутов в уже созданные классы и созданием новых классов для оставшихся в модели типов сущностей.
Атрибут DisplayFormat
Для дат записи студентов, все страницы сейчас отображают время вместе с датой, тогда как нам нужна только дата. С помощью аннотаций можно указать в одном месте настройку, которая будет распространяться на всё. Для этого добавим атрибут к свойству EnrollmentDate в классе Student.
Строка форматирования указывает, что необходимо отображать только короткую форму даты этого свойства. Настройка ApplyFormatInEditMode указывает, что данное форматирование должно применяться также для значений, отображающихся в текстовых строках, предназначенных для редактирования. (допустим, нам не нужно отображать в некоторых полях, например, в которых содержатся значения, связанные с валютой, знак валюты)
Убедитесь, что на страницах About и Student время в датах больше не показывается.
Атрибут MaxLength
Атрибутами можно определить правила валидации, допустим, в том случае, если необходимо ограничить введённое пользователем значение 50-ю символами. Чтобы воспользоваться этой возможностью, добавьте атрибут Range в свойствам LastName и FirstMidName:
Откройте страницу Create и попробуйте ввести два имени из более чем 50-ти символов, затем нажмите Create чтобы убедиться что валидация работает.
Указывать максимальное значение для вводимых элементов – одна из хороших практик. В ином случае, если используется подход Code First, при создании базы данных имена соответствующих столбцов будут иметь максимально допустимую длину.
Атрибут Column
Атрибутами можно контролировать маппинг классов на базу данных. Допустим, вы использовали имя FirstMidName для поля «Имя» потому, что это поле могло также содержать отчество. Но вы хотите, чтобы в базе данных соответствующий столбец назывался FirstName, потому что пользователи пользуются в своих запросах именно этим наименованием. Для этого можно воспользоваться атрибутом Column.
Атрибут Column указывает, что при создании базы данных, столбец таблицы Student, который маппится на свойство FirstMidName, будет называться FirstName.
Добавьте этот атрибут к свойству FirstMidName:
Откройте страницу Student Index, на которой ничего не изменилось (необходимо не просто запустить сайт и посмотреть на домашнюю страницу, необходимо перейти на страницу Student Index чтобы инициировать обращение к базе данных, что инициирует также ее удаление и пересоздание). Однако, если вы откроете базу данных ServerExplorer вы увидите, что в таблице Student столбец называется FirstName.
В окне Propertiesможно заметить, что длина ограничена 50-тью символами благодаря использованию атрибута MaxLength.
Создание сущности Instructor
Создайте Models\Instructor.csсо следующим кодом:
Обратите внимание, что некоторые из свойств аналогичны свойствам сущности Student. В Implementing Inheritance мы используем наследование для решения проблемы избыточности.
Атрибуты Required и Display
Атрибуты свойства LastName указывают на то, что это необходимое для заполнения значение, заголовок для текстового поля должен быть равен “Last Name” (вместо названия свойства “LastName”) и длина введённого значения должна быть не более 50-ти символов..
Вычисляемое свойство FullName
FullName является вычисляемым свойством, которое содержит значение, вычисляемое путем соединения двух других свойств. Поэтому оно имеет только аксессор, в базе данных же не создаётся соответствующего столбца.
Navigation Properties Courses и OfficeAssignment
Свойства Courses и OfficeAssignment — navigation properties. Как было упомянуто ранее, они обычно помечаются модификатором virtual чтобы использовать lazy loading. Кроме этого, если данное свойство может содержать несколько сущностей, его тип должен быть Icollection.
Поскольку преподаватель может вести несколько курсов, переменная Courses определена как коллекция сущностей Course. С другой стороны, преподаватель может иметь только один офис, поэтому OfficeAsignment определн как единственная сущность типа OfficeAssignment (которая может быть равна null, если у преподавателя нет офиса).
Создание сущности OfficeAssignment
Создайте Models\OfficeAssignment.cs со следующим содержанием:
Атрибут Key
Между сущностями Instructor и OfficeAssignment существует связь один-к-нулю-или-единице. Назначенный офис существует только в сочетании с назначенным преподавателем, поэтому его первичный ключ также является внешним ключом на сущность Instructor. Entity Framework, однако, не может автоматически распознать InstructorID как первичный ключ поскольку имя данного свойства не соответствует конвенциям именования первичных ключей, не равно ID и classnameID. Поэтому атрибутом Key указывается, что данный атрибут является первичным ключом.
Атрибут Key можно использовать также для той ситуации, когда у вас уже есть первичный ключ, но вы хотите назвать свойство иначе чем classnameID или ID.
Instructor Navigation Property
Сущность Instructor nullable OfficeAssignment navigation property (преподаватель может не иметь офиса), а сущность OfficeAssignment имеет не-nullable Instructor navigation property (офис не может существовать без преподавателя). Когда Instructor имеет ассоциированую с ним сущность OfficeAsignment, оба имеют друг на друга ссылки в своих navigation property.
-
или Visual Web Developer Express 2010 SP1 (если вы используете для установки одну из этих ссылок, следующий софт установится автоматически)
The Contoso University
Приложение, которые вы разработаете, является простым вебсайтом университета.
Пользователи смогут просматривать и обновлять данные о студентах, курсах и преподавателях. Несколько скриншотов того, что получится, ниже.
UI близок по стилю к тому, что генерируется шаблонами по умолчанию, поэтому акцент будет на вопросах использования Entity Framework.
Подходы к разработке с Entity Framework
Исхоя из диаграммы, имеется три подхода к работе с данными в Entity Framework: Database First, Model First, и Code First.
Database First
В случае уже имеющейся базы данных Entity Framework может автоматически создать модель данных, состоящую из классов и свойств, соответствующих объектам базы даных (таким, как таблицы и столбцы). Информация о структуре базы (store schema), модель данных (conceptual model) и маппинг их друг на друга содержится в XML в файле .edmx. Visual Studio предоставляет графический дизайнер Entity Framework, с помощью которого можно просматривать и редактировать .edmx. Части Getting Started With the Entity Framework и Continuing With the Entity Framework в материалах о Web Forms используют подход Database First.
Model First
Если базы нет, вы можете начать с создания модели данных, используя дизайнер Entity Framework Visual Studio. После окончания работ над моделью дизайнер сгенерирует DDL (data definition language)-код для создания базы. В этом подходе для хранения информации о модели и маппингах также используется .edmx. What's New in the Entity Framework 4 включает небольшой пример разработки с использованием данного подхода.
Code First
Вне зависимости от наличия базы вы можете вручную написать код классов и свойств, соответствующих сущностям в базе и использовать этот код с Entity Framework без использования файла .edmx. Именно поэтому можно порой увидеть, как этот подход называют code only, хотя официальное наименование Code First. Маппинг между store schema и conceptual model в represented by your code is handled by convention and by a special mapping API. Если базы ещё нет, Entity Framework может создать, удалить или пересоздать её в случае изменений в модели.
API доступа к данным, разработанное для Code First, основано на классе DbContext . API может быть использован также и в процессе разработки с подходами Database First и Model First. Для дополнительной информации смотрите When is Code First not code first? В блоге команды разработки Entity Framework.
POCO (Plain Old CLR Objects)
По умолчанию для подходов Database First и Model First классы модели данных наследуются от EntityObject, который и предоставляет функциональность Entity Framework. Это значит, что эти классы не являются persistence ignorant и, таким образом, не полностью соответствуют одном из условий domain-driven design. Все подходы к разработке с Entity Framework также могут работать с POCO (plain old CLR objects), что, в целом, значит, что они являются persistence-ignorant из-за отсутствия наследования EntityObject .
Создание веб-приложения MVC
Настройка стилей
Несколько небольших поправок изменят меню сайта, расположение элементов и домашнюю страницу.
Для настройки меню Contoso University, в Views\Shared\_Layout.cshtml замените текст в h 1 и ссылки в меню, как в примере:
В Views\Home\Index.cshtml удалите всё в теге h2 .
В Controllers\HomeController.cs замените "Welcome to ASP.NET MVC!" на "Welcome to Contoso University!"
В Content\Site.css для смещения меню влево совершите следующие изменения:
Создание модели данных
Далее создадим первые классы-сущности для Contoso University. Мы начнём со следующих трёх сущностей:
Установлена связь один-ко-многим между сущностями Student и Enrollment , и связь один-ко-многим между Course и Enrollment . Другими словами, студент может посещать любое количество курсов, и курс может иметь любое количество студентов, посещающих его.
В дальнейшем вы создадите классы для каждой из этих сущностей.
Note: компиляция проекта без созданных классов для этих сущностей вызовет ошибки компиляторов.
Сущность Student
В папке Models создайте Student.cs и замените сгенерированный код на:
Свойство StudentID будет первичным ключом соответствующей таблицы. По умолчанию, Entity Framework воспринимает свойство с ID или classname ID как первичный ключ.
Свойство Enrollments — navigation property. Navigation properties содержат другие сущности, относящиеся к текущей. В данном случае свойство Enrollments содержит в себе все сущности Enrollment , ассоциированные с текущей сущностью Student . Другими словами, если некая запись Student в базе данных имеет связь с двумя записями Enrollment (записей, содержащих значения первичных ключей для студента в поле внешнего ключа StudentID ), свойство этой записи Enrollments будет содержать две сущности Enrollment .
Navigation properties обычно помечаются модификатором virtual дабы использовать возможность Entity Framework, называемую lazy loading. (суть Lazy loading будет объяснена позже, в Reading Related Data) Если navigation property может содержать несколько сущностей (в связях многие-ко-многим и один-ко-многим), его тип должен быть ICollection .
Сущность Enrollment
В папке Models создайте Enrollment.cs со следующим содержанием:
Знак вопроса после указания типа decimal указывает на то, что свойство Grade nullable. Оценка, поставленная в null отличная от нулевой оценки— null обозначает то, что оценка еще не выставлена, тогда как 0 – уже значение.
Свойство StudentID является внешним ключом, и соответствующее navigation property Student . Сущность Enrollment ассоциирована с одной сущностью Student , поэтому свойство может содержать только одну сущность указанного типа (в отличие Student . Enrollments ).
Свойство CourseID является внешним ключом, и соответствующее navigation property Course . Сущность Enrollment ассоциирована с одной сущностью Course .
Сущность Course
В папке Models создайтеCourse.cs со следующим содержанием:
Свойство Enrollments — navigation property. Сущность Course может ассоциироваться с бесконечным множеством сущностей Enrollment .
Создание Database Context
Главный класс, координирующий функциональность Entity Framework для текущей модели данных называется database context. Данный класс наследуется от System . Data . Entity . DbContext . В коде вы определяете, какие сущности включить в модель данных, и также можете определять поведение самого Entity Framework. В нашем коде этот класс имеет название SchoolContext .
Создайте папку DAL и в ней новый класс SchoolContext.cs:
Код создаёт свойство DbSet для каждого множества сущностей. В терминологии Entity Framework множество сущностей (entity set) относится к таблице базы данных, и сущность относится к записи в таблице.
Содержимое метода OnModelCreating защищает имена таблиц от плюрализации, и, если вы этого не делаете, то получаете такие имена таблиц, как Students , Courses , Enrollments . В ином случае имена таблиц будут Student , Course , Enrollment . Разработчики спорят на тему того, нужно ли плюрализовывать имена таблиц или нет. Мы используем одиночную форму, но важен тот момент, что вы можете выбрать, включать эту строчку в код или нет.
(Этот класс находится в namespace Models потому, что в некоторых ситуациях подход Code First подразумевает нахождение классов сущностей и контекста в одном и том же namespace.)
Определение Connection String
Вам не нужно определять connection string. Если вы не определили эту строку, Entity Framework автоматически создаст базу данных SQL Server Express. Мы, однако, будем работать с SQL Server Compact, и вам необходимо будет создать строку подключения с указанием на это.
Откройте Web.config и добавьте новую строку подключения в коллекцию connectionStrings . (Убедитесь, что вы обновляете Web.config в корне проекта, так как есть еще один Web.config в папке Views, который трогать не надо.)
По умолчанию Entity Framework ищет строку подключения, названную также как object context class. Строка подключения, которую вы добавили, определяет базу данных School.sdf, находящуюся в папке App_data и SQL Server Compact.
Инициализация базы данных с тестовыми данными
Entity Framework может автоматически создать базу данных при запуске приложения. Вы можете указать, что это должно выплоняться при каждом запуске приложения или только тогда, когда модель рассинхронизирована с существующей базой. Вы можете также написать класс с методом, который Entity Framework будет автоматически вызывать перед созданием базы для использования её с тестовыми данными. Мы укажем, что база должна удаляться и пересоздаваться при изменении модели.
В папке DAL создайте новый класс SchoolInitializer.cs с кодом, с помощью которого база будет создана при необходимости и заполнена тестовыми данными.
Метод Seed принимает объект контекста базы как входящий параметр и использует его для добавления новых сущностей в базу. Для каждого типа сущности код создает коллекцию новых сущностей, добавляя их в соответствующее свойство DbSet, и потом сохраняет изменения в базу. Нет необходимости в вызове SaveChanges после каждой группы сущностей, как сделано у нас, но это помогает определить проблему в случае возникновения исключений.
Измените Global.asax.cs для того, чтобы наш код вызывался при каждом запуске приложения:
- Добавьте using :
- В методе Application_Start вызовите метод Entity Framework, который запускает код инициализации базы:
Note при развертывании приложения на продакшн-сервер вы должны удалить весь код, который инициализирует базу тестовыми данными.
Далее вы создадите веб-страницу для отображения данных, и процесс запроса данных автоматически инициирует создание базы. Вы начнете с нового контроллера, но перед этим, соберите проект для того, чтобы модель и контекстные классы стали доступны для MVC controller scaffolding.
Создание контроллера Student
Для создание контроллера Student , щелкните на папке Controllers в Solution Explorer, нажмите Add, Controller. В Add Controller совершите следующие действия и изменения и нажмите Add:
- Controller name: StudentController.
- Template: Controller with read/write actions and views, using Entity Framework. (по умолчанию.)
- Model class: Student (ContosoUniversity.Models). (если этого нет, пересоберите проект)
- Data context class: SchoolContext (ContosoUniversity.Models).
- Views: Razor (CSHTML). (по умолчанию)
Откройте Controllers\StudentController.cs, вы увидите созданную переменную, инициализирующую объект контекста базы данных:
Действие Index собирает список студентов из свойства Students из экземпляра контекста базы данных:
Автоматическое scaffolding было создано для множества Student . Для настройки заголовков и последовательности колонок откройте Views\Student\Index.cshtml и замените код на:
Запустите сайт, нажмите на вкладку Students.
Закройте браузер. В Solution Explorer выберите проект ContosoUniversity. Нажмите Show all Files, Refresh и затем разверните папку App_Data.
Два раза щелкните на School.sdf для открытия Server Explorer, и Tables.
Note если у вас возникает ошибка после того, как вы два раза щелкаете на School.sdf, убедитесь, что вы установили Visual Studio 2010 SP1 Tools for SQL Server Compact 4.0. Если все установлено, перезапустите Visual Studio.
Для каждой таблицы есть свое множество сущностей + одна дополнительная таблица. EdmMetadata используется для определения Entity Framework, когда случилась разсинхронизация модели и базы.
Щелкните на одной из таблиц и Show Table Data чтобы увидеть загруженные классом SchoolInitializer данные.
Закройте подключение, иначе может возникнуть проблема с запуском приложения.
Соглашения
Количество кода, нужное для создания Entity Framework базы, минимально из-за использования (conventions) Entity Framework. Некоторые из них уже были упомянуты:
- Форма множественного числа имен классов сущностей используется в качестве имен таблиц.
- Имена свойств сущностей используется в качестве имен столбцов.
- Свойства сущностей с именами ID или classname ID распознаются как первичные ключи.
- Entity Framework подключается к базе, отыскав строку подключения с таким же именем, как и класс контекста (в данном случае SchoolContext ).
Вы создали простое приложение с использованием Entity Framework и SQL Server Compact для хранения и отображения данных. Далее мы научимся совершать простые CRUD (create, read, update, delete) операции.
В Models\Course.cs замените ранее созданный код на:
Атрибут DatabaseGenerated
Атрибут DatabaseGenerated с параметром None, указанный для свойства CourseID, определяет то, что значение первичного ключа задаётся пользователем, а не генерируется базой данных.
По умолчанию Entity Framework предполагает автогенерацию первичных ключей базой данных, что и необходимо в большинстве ситуаций. Однако для сущности Course используются численные заданные пользователем значения, такие как 1000 для одного факультета, 2000 для другого и так далее.
Внешний ключ и Navigation Properties
Свойства-внешние ключи и navigation properties в сущности Course отражают следующие связи:
Курс ассоциирован с одним факультетом, таким образом, имеется внешний ключ DepartmentID и Department navigation property:
public int DepartmentID < get; set; >
public virtual Department Department
Курс может посещать неограниченное количество студентов, поэтому имеется Enrollments navigation property:
public virtual ICollection Enrollments
Курс может вестись различными преподавателями, поэтому имеется Instructors navigation property:
public virtual ICollection Instructors
Создание сущности Department
Создайте Models\Department.cs со следующим кодом:
Атрибут Column
Ранее атрибут Column мы использовали для изменения маппинга имени столбца. В коде для сущности Department этот атрибут используется для изменения маппинга типа данных SQL, то есть столбец будет определён в базе данных с типом данных SQL Server:
Обычно это не нужно, потому что Entity Framework автоматически подбирает наиболее подходящий тип данных исходя из типа CLR, который определён для свойства. Допустим, CLR тип decimal станет SQL Server типом decimal. Но в данном случае вы точно знаете, что свойство будет содержать цифры, связанные с валютой, и тип money будет наиболее подходящим для этого свойства.
Внешний ключ и Navigation Properties
Внешними ключами и navigation properties отражены следующие связи:
Факультет может как содержать, так и не содержать администратора, и администратор всегда = преподаватель. Поэтому свойство InstructorID определено как внешний ключ для сущности Instructor, и знак вопроса после типа int указывает на то, что свойство может быть nullable. Navigation property Administrator содержит сущность Instructor:
public int? InstructorID
public virtual Instructor Administrator < get; set; >
Факультет может иметь множество курсов, поэтому присутствует Courses navigation property:
public virtual ICollection Courses
Note Конвенциями определено, что Entity Framework каскадно удаляет non-nullable внешние ключи и в случаях связи многие-ко-многим. Это может привести к итеративному каскадному удалению, вызвав исключение при запуске кода. Допустим, если не определить Department.InstructorID как nullable, вы получите следующее исключение при: "The referential relationship will result in a cyclical reference that's not allowed."
Изменения, связанные с сущностью Student
В Models\Student.cs замените код на:
Изменения, касающиеся сущности Enrollment
В Models\Enrollment.cs замените код на:
Внешние ключи и Navigation Properties
Внешние ключи и navigation properties отражают следующие связи:
Каждой сущности записи на курс соответствует один курс, поэтому присутствует внешний ключ CourseID и Course navigation property:
public int CourseID
public virtual Course Course < get; set; >
Каждой сущности записи на курс соответствует один студент, поэтому присутствует внешний ключ StudentID и Student navigation property:
public int StudentID
public virtual Student Student
Связи многие-ко-многим
Сущности Student и Course связаны друг с другом связью многие-ко-многим, и сущность Enrollment соответствует and the Enrollment entity corresponds to a many-to-many join table with payload in the database. Это значит, что таблица Enrollment содержит дополнительные данные помимо внешних ключей для объединённых таблиц (в нашем случае первичный ключ и свойство Grade).
На изображении ниже представлены связи в виде диаграммы сущностей, сгенерированной Entity Framework designer.
Линия каждой связи имеет 1 на одном конце и * на другом, обозначая связь один-ко-многим.
Если таблица Enrollment не содержит информацию об оценках, необходимо иметь только два внешних ключа CourseID и StudentID. In that case, it would correspond to a many-to-many join table without payload (or a pure join table) in the database, and you wouldn't have to create a model class for it at all. Сущности Instructor и Course связаны подобной связью многие-ко-многим, и, как вы видите, между ними нет класса сущности:
Хотя объединённая таблица необходима:
Entity Framework автоматически создаёт таблицу CourseInstructor, доступ к которой осуществляется косвенно, а именно через Instructor.Courses и Course.Instructors navigation properties.
Атрибут DisplayFormat
Атрибут DisplayFormat для свойства Grade определяет форматирование для элемента:
- Оценка выводится в формате "3.5" или "4.0".
- Такое же форматирование оценка имеет и в режиме.
- Если оценки нет, выводится надпись "No grade".
Связи на диаграмме сущностей
Диаграмма ниже демонстрирует систему связей для модели School.
Кроме связей многие-ко-многим (*-*) и один-ко-многим (1-*), можно также увидеть связь один-к-нулю-или-одному (1-0..1) между сущностями Instructor и OfficeAssignment и нуль-к-одному-или-ко-многим (0..1 — *) Department и Instructor.
Настройка контекста базы данных
Дальше мы добавим новые сущности в класс SchoolContext и настроим мапинг. В некоторых случаях необходимо будет использовать методы вместо атрибутов из-за того, что для некоторой функциональности атрибутов просто не сущнствует. В других случаях можно выбирать, использовать методы или атрибуты (некоторые люди предпочитают не использовать атрибуты).
Замените код в DAL\SchoolContext.cs на:
В методе OnModelCreating определяются следующие связи:
Один-к-нулю-или-одному между Instructor и OfficeAssignment:
modelBuilder.Entity().HasOptional(p => p.OfficeAssignment).WithRequired(p => p.Instructor);
Многие-ко-многим между Instructor и Course. Код определяет таблицу и столбцы для объединённой таблицы. Code First может настроить связь многие-ко-многим и без кода, но если вы его не вызовете, то для столбцов будут взяты стандартные имена, такие как InstructorInstructorID для InstructorID.
modelBuilder.Entity()
.HasMany(c => c.Instructors).WithMany(i => i.Courses)
.Map(t => t.MapLeftKey("CourseID")
.MapRightKey("InstructorID")
.ToTable("CourseInstructor"));
Один-к-нулю-или-одному между Department и Instructor, с помощью Department.Administrator navigation property:
Заполнение базы данных тестовыми данными
Перед этим вы создавали DAL\SchoolInitializer.cs для заполнения базы тестовыми данными. Теперь замените старый код на новый, который учитывает присутствие новых сущностей.
Обратите внимание на обработку сущности Course, которая связана связью многие-ко-многим с сущностью Instructor:
При создании объекта Course как пустая коллекция (Instructors = new List()), что делает возможным добавление сущностей Instructor, связанных с Course, с помощью метода Instructor.Add(). Если вы не создали пустой List, у вас не получится добавлять подобные отношения, потому что свойство Instructors будет равно null и не будет иметь метода Add.
Note Не забывайте перед развертыванием проекта на production-сервер удалять весь код инициализации базы.
Удаление и пересоздание базы данных
Запустите проект и выберите страницу Student Index.
Если страница не открывается или вы получаете ошибку о том, что файл School.sdf уже используется (изображение ниже), необходимо еще раз открыть Server Explorer и закрыть подключение к базе и затем попробовать снова открыть страницу.
После этого откройте базу в Server Explorer и посмотрите в Tables новые таблицы.
Кроме EdmMetadata обратите внимание на таблицу, для которой вы не создавали класса CourseInstructor. Это объединённая из Instructor и Course таблица.
Щелкните на таблице CourseInstructor и нажмите Show Table Data чтобы убедиться в наличии данных, добавленных ранее Course.Instructors navigation property.
Теперь у вас есть сложная модель данных и соответствующая база данных. Дальше мы научимся разным способам обращения к данным.
I will demonstrate how to
For the application development, I will use
For a better understanding of this post, I suggest you read the below post first:
If you are ready, let’s get started.
Adding a Model and Scaffolding
Now, we will implement our data model class (Model part of the MVC app). We will use this class with Entity Framework Core (EF Core) to work with a database. EF Core is an object-relational mapping (ORM) framework that simplifies the data access code. Model classes don’t have any dependency on EF Core. They just define the properties of the data that will be stored in the database.
In this post, we will write the model classes first and EF Core will create the database. This is called Code First Approach.
Now, right click the Models folder and select Add->Class.
Give the name TvShow.cs to the class and select OK. Then add the following properties to the class.
Then add another class called Genre.cs as below:
At this point, we need to build the project.
Next, we will create our controller and views (View-Controller part of the MVC app).
Right click the Controllers Folder and select Add -> Controller.
In the following window, select MVC Controller with views, using Entity Framework and click Add.
In the next window, select TvShow as Model class and click + sign in the Data context class and give it a name. Keep the default for the other fields and select Add.
At this point, scaffolding tool works. The automatic creation of the database context and CRUD (create, read, update, and delete) action methods and views is known as scaffolding.
We will now examine what Visual Studio added to our project automatically as the result of scaffolding:
- Controller class ->Controllers/TvShowsController.cs
The following methods are the action methods of the TvShowsController:
- Index (GET)
- Details (GET)
- Create (GET & POST)
- Edit (GET & POST)
- Delete (GET & POST)
2. Razor view files for Create, Delete, Details, Edit, and Index pages -> Views/TvShows/*.cshtml
3. Dll.s related to Entity Framework Core (under Dependencies menu)
4. EF Core database context class -> Data/TvShowsContext.cs
The TvShowsContext coordinates EF Core functionality (Create, Read, Update, Delete, etc.) for the TvShow model. The TvShowsContext is derived from Microsoft.EntityFrameworkCore.DbContext. The data context specifies which entities are included in the data model:
The preceding code creates a DbSet property for the entity set. In Entity Framework terminology, an entity set typically corresponds to a database table and an entity corresponds to a row in the table.
The scaffolding tool registered the DB context (TVShowsContext) with the DI container in the ConfigureServices method of Startup.cs as below:
In the Controllers/TvShowsController.cs , the constructor uses dependency injection to inject the database context ( TvShowsContext ) into the controller.
TvShowsContext is used in each of the CRUD methods in the TvShowsController.
Creating the Starter Project
First, open File -> New -> Project.
At this point, we have a starter project. Let’s run the project (Crtl + F5) to see everything is OK.
You can change the browser that you want to run the site from below:
After running we get the Welcome page as below:
MVC invokes controller classes (and the action methods within them) depending on the incoming URL. The default URL routing logic used by MVC uses a format like this to determine what code to invoke:
The routing format is set in the Configure method in Startup.cs file.
In the Views/Shared/_Layout.cshtml file, make the following changes in the title, menu link and the footer.
After the changes, our site looks like below:
We changed the menu link’s controller to TvShows and we will implement that in the next section.
Читайте также: