Создание модели данных Entity Framework для приложения ASP.NET MVC (1 из 10)

Том Дайкстра (Tom Dykstra)

Примечание

Доступна более новая версия этой серии руководств для Visual Studio 2013, Entity Framework 6 и MVC 5.

Пример веб-приложения Университета Contoso демонстрирует создание ASP.NET приложений MVC 4 с помощью Entity Framework 5 и Visual Studio 2012. В этом примере приложения реализуется веб-сайт вымышленного университета Contoso. На нем предусмотрены различные функции, в том числе прием учащихся, создание курсов и назначение преподавателей. В этой серии учебников объясняется, как создать пример приложения Университета Contoso.

Code First

Существует три способа работы с данными в Entity Framework: Database First, Model First и Code First. Это руководство предназначено для Code First. Сведения о различиях между этими рабочими процессами и рекомендации по выбору оптимального для вашего сценария см. в разделе Рабочие процессы разработки Entity Framework.

MVC

Пример приложения основан на ASP.NET MVC. Если вы предпочитаете работать с моделью ASP.NET Web Forms, ознакомьтесь с серией учебников Привязка модели и веб-формы и ASP.NET схеме содержимого доступа к данным.

Версии программного обеспечения

Показано в руководстве Также работает с
Windows 8 Windows 7
Visual Studio 2012 Visual Studio 2012 Express for Web. Пакет SDK для Windows Azure автоматически устанавливается, если у вас еще нет VS 2012 или VS 2012 Express для Интернета. Visual Studio 2013 должно работать, но учебник не был протестирован с его помощью, и некоторые меню и диалоговые окна отличаются. Для развертывания Windows Azure требуется версия VS 2013 пакета SDK для Windows Azure .
.NET 4.5 Большинство показанных функций будут работать в .NET 4, а некоторые — нет. Например, для поддержки перечисления в EF требуется .NET 4.5.
Entity Framework 5
Windows Azure SDK 2.1 Если вы пропустите шаги по развертыванию Windows Azure, вам не потребуется пакет SDK. После выпуска новой версии пакета SDK по ссылке будет установлена более новая версия. В этом случае может потребоваться адаптировать некоторые инструкции к новым пользовательским интерфейсам и функциям.

Вопросы

Если у вас есть вопросы, которые не связаны непосредственно с этим руководством, вы можете опубликовать их на форуме ASP.NET Entity Framework, Entity Framework и LINQ to Entities форуме или StackOverflow.com.

Благодарности

В последнем руководстве серии приведены подтверждения и заметки о VB.

Веб-приложение университета Contoso

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

Пользователи приложения могут просматривать и обновлять сведения об учащихся, курсах и преподавателях. Будет создано несколько экранов.

Students_Index_page

Снимок экрана: страница поиска учащихся в примере веб-приложения Университета Contoso и страница

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

Предварительные требования

В руководствах и снимках экрана в этом руководстве предполагается, что вы используете Visual Studio 2012 или Visual Studio 2012 Express для Интернета с последним обновлением и пакетом AZURE SDK для .NET, установленными по состоянию на июль 2013 г. Все это можно получить по следующей ссылке:

Пакет Azure SDK для .NET (Visual Studio 2012)

Если у вас установлена Среда Visual Studio, по приведенной выше ссылке будут установлены все недостающие компоненты. Если у вас нет Visual Studio, по ссылке будет установлена Visual Studio 2012 Express для Интернета. Можно использовать Visual Studio 2013, но некоторые необходимые процедуры и экраны будут отличаться.

Создание веб-приложения MVC

Откройте Visual Studio и создайте проект C# с именем ContosoUniversity с помощью шаблона веб-приложения ASP.NET MVC 4 . Убедитесь, что вы используете платформа .NET Framework 4.5 (вы будете использовать enum свойства, а для этого требуется .NET 4.5).

New_project_dialog_box

В диалоговом окне Создать проект ASP.NET MVC 4 выберите шаблон Интернет-приложение .

Оставьте выбранным подсистему представлений Razor и оставьте поле Создание проекта модульного теста проверка снято.

Нажмите кнопку ОК.

Project_template_options

Настройка стиля сайта

Выполните незначительную настройку меню, макета и домашней страницы сайта.

Откройте Views\Shared\_Layout.cshtml и замените содержимое файла приведенным ниже кодом. Изменения выделены.

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>@ViewBag.Title - Contoso University</title>
        <link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
        <meta name="viewport" content="width=device-width" />
        @Styles.Render("~/Content/css")
        @Scripts.Render("~/bundles/modernizr")
    </head>
    <body>
        <header>
            <div class="content-wrapper">
                <div class="float-left">
                    <p class="site-title">@Html.ActionLink("Contoso University", "Index", "Home")</p>
                </div>
                <div class="float-right">
                    <section id="login">
                        @Html.Partial("_LoginPartial")
                    </section>
                    <nav>
                        <ul id="menu">
                            <li>@Html.ActionLink("Home", "Index", "Home")</li>
                            <li>@Html.ActionLink("About", "About", "Home")</li>
                            <li>@Html.ActionLink("Students", "Index", "Student")</li>
                            <li>@Html.ActionLink("Courses", "Index", "Course")</li>
                            <li>@Html.ActionLink("Instructors", "Index", "Instructor")</li>
                            <li>@Html.ActionLink("Departments", "Index", "Department")</li>
                        </ul>
                    </nav>
                </div>
            </div>
        </header>
        <div id="body">
            @RenderSection("featured", required: false)
            <section class="content-wrapper main-content clear-fix">
                @RenderBody()
            </section>
        </div>
        <footer>
            <div class="content-wrapper">
                <div class="float-left">
                    <p>&copy; @DateTime.Now.Year - Contoso University</p>
                </div>
            </div>
        </footer>

        @Scripts.Render("~/bundles/jquery")
        @RenderSection("scripts", required: false)
    </body>
</html>

Этот код вносит следующие изменения:

  • Заменяет экземпляры шаблонов "My ASP.NET MVC Application" и "your logo here" на "Contoso University".
  • Добавляет несколько ссылок на действия, которые будут использоваться далее в этом руководстве.

В файле Views\Home\Index.cshtml замените содержимое файла следующим кодом, чтобы исключить абзацы шаблона о ASP.NET и MVC:

@{
    ViewBag.Title = "Home Page";
}
@section featured {
    <section class="featured">
        <div class="content-wrapper">
            <hgroup class="title">
                <h1>@ViewBag.Title.</h1>
                <h2>@ViewBag.Message</h2>
            </hgroup>
        </div>
    </section>
}

В разделе Контроллеры\HomeController.cs измените значение ViewBag.Message в методе Index Action на "Добро пожаловать в университет Contoso!", как показано в следующем примере:

public ActionResult Index()
{
    ViewBag.Message = "Welcome to Contoso University";

    return View();
}

Нажмите клавиши CTRL+F5, чтобы запустить сайт. Вы увидите домашнюю страницу с меню main.

Contoso_University_home_page

Создание модели данных

Теперь необходимо создать классы сущностей для приложения университета Contoso. Вы начнете со следующих трех сущностей:

Class_diagram

Между сущностями Student и Enrollment, а также между сущностями Course и Enrollment существует отношение "один ко многим". Другими словами, учащийся может быть зарегистрирован в любом количестве курсов, а в отдельном курсе может быть зарегистрировано любое количество учащихся.

В следующих разделах создаются классы для каждой из этих сущностей.

Примечание

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

Сущность Student

Student_entity

В папке Models создайте Student.cs и замените существующий код следующим кодом:

using System;
using System.Collections.Generic;

namespace ContosoUniversity.Models
{
    public class Student
    {
        public int StudentID { get; set; }
        public string LastName { get; set; }
        public string FirstMidName { get; set; }
        public DateTime EnrollmentDate { get; set; }
        
        public virtual ICollection<Enrollment> Enrollments { get; set; }
    }
}

Свойство StudentID будет использоваться в качестве столбца первичного ключа в таблице базы данных, соответствующей этому классу. По умолчанию Entity Framework интерпретирует свойство с именем ID или classnameID в качестве первичного ключа.

Свойство Enrollments является свойством навигации. Свойства навигации содержат другие сущности, связанные с этой сущностью. В этом случае Enrollments свойство сущности Student будет содержать все сущности Enrollment , связанные с этой Student сущностью. Другими словами, если заданная Student строка в базе данных содержит две связанные Enrollment строки (строки, содержащие значение первичного ключа этого учащегося во внешнем StudentID ключевом столбце), свойство Student навигации этой сущности Enrollments будет содержать эти две Enrollment сущности.

Свойства навигации обычно определяются как virtual , чтобы они могли воспользоваться преимуществами определенных функций Entity Framework, таких как отложенная загрузка. (Отложенная загрузка будет описана далее в руководстве по чтению связанных данных далее в этой серии.

Если свойство навигации может содержать несколько сущностей (как в отношениях "многие ко многим" или "один ко многим"), оно должно иметь тип списка, допускающий добавление, удаление и обновление записей, такой как ICollection.

Сущность регистрации

Enrollment_entity

В папке Models создайте файл Enrollment.cs и замените существующий код следующим кодом:

namespace ContosoUniversity.Models
{
    public enum Grade
    {
        A, B, C, D, F
    }

    public class Enrollment
    {
        public int EnrollmentID { get; set; }
        public int CourseID { get; set; }
        public int StudentID { get; set; }
        public Grade? Grade { get; set; }
        
        public virtual Course Course { get; set; }
        public virtual Student Student { get; set; }
    }
}

Свойство Grade является перечислением. Знак вопроса после объявления типа Grade указывает, что свойство Gradeдопускает значение NULL. Оценка со значением NULL отличается от нулевой оценки. Значение NULL означает, что оценка не известна или еще не назначена.

Свойство StudentID представляет собой внешний ключ. Ему соответствует свойство навигации Student. Сущность Enrollment связана с одной сущностью Student, поэтому это свойство может содержать одну сущность Student (в отличие от представленного ранее свойства навигации Student.Enrollments, которое может содержать несколько сущностей Enrollment).

Свойство CourseID представляет собой внешний ключ. Ему соответствует свойство навигации Course. Сущность Enrollment связана с одной сущностью Course.

Сущность Course

Course_entity

В папке Models создайте Course.cs, заменив существующий код следующим кодом:

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;

namespace ContosoUniversity.Models
{
    public class Course
    {
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int CourseID { get; set; }
        public string Title { get; set; }
        public int Credits { get; set; }
        
        public virtual ICollection<Enrollment> Enrollments { get; set; }
    }
}

Свойство Enrollments является свойством навигации. Сущность Course может быть связана с любым числом сущностей Enrollment.

Мы расскажем больше о [DatabaseGenerated(DatabaseGeneratedOption. Атрибут None)] в следующем руководстве. Фактически, этот атрибут позволяет ввести первичный ключ для курса, а не использовать базу данных, чтобы создать его.

Создание контекста базы данных

Класс main, который координирует функциональные возможности Entity Framework для данной модели данных, является классом контекста базы данных. Этот класс создается путем наследования от класса System.Data.Entity.DbContext . В коде указываются сущности, которые включаются в модель данных. Также вы можете настроить реакцию платформы Entity Framework на некоторые события. В этом проекте соответствующий класс называется SchoolContext.

Создайте папку с именем DAL (для уровня доступа к данным). В этой папке создайте файл класса с именем SchoolContext.cs и замените существующий код следующим кодом:

using ContosoUniversity.Models;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;

namespace ContosoUniversity.DAL
{
    public class SchoolContext : DbContext
    {
        public DbSet<Student> Students { get; set; }
        public DbSet<Enrollment> Enrollments { get; set; }
        public DbSet<Course> Courses { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        }
    }
}

Этот код создает свойство DbSet для каждого набора сущностей. В терминологии Entity Framework набор сущностей обычно соответствует таблице базы данных, а сущность соответствует строке в таблице.

Оператор modelBuilder.Conventions.Remove в методе OnModelCreating предотвращает множественное число имен таблиц. Если этого не сделать, созданные таблицы будут называться Students, Coursesи Enrollments. Вместо этого в таблицах будут Studentиспользоваться имена , Courseи Enrollment. В среде разработчиков нет единого мнения о том, следует ли использовать имена таблиц во множественном числе. В этом руководстве используется единственная форма, но важно то, что вы можете выбрать любую форму, включив или опустив эту строку кода.

SQL Server Express LocalDB

LocalDB — это упрощенная версия ядра СУБД SQL Server Express, которая запускается по запросу и работает в пользовательском режиме. LocalDB выполняется в специальном режиме выполнения SQL Server Express, который позволяет работать с базами данных как с .mdf файлами. Как правило, файлы базы данных LocalDB хранятся в папке App_Data веб-проекта. Функция пользовательского экземпляра в SQL Server Express также позволяет работать с .mdf файлами, но эта функция экземпляра пользователя является устаревшей, поэтому для работы с файлами .mdf рекомендуется использовать LocalDB.

Как правило, SQL Server Express не используется для рабочих веб-приложений. LocalDB, в частности, не рекомендуется использовать в рабочей среде с веб-приложением, так как она не предназначена для работы с IIS.

В Visual Studio 2012 и более поздних версиях LocalDB устанавливается по умолчанию вместе с Visual Studio. В Visual Studio 2010 и более ранних версиях SQL Server Express (без LocalDB) устанавливается по умолчанию вместе с Visual Studio. Если вы используете Visual Studio 2010, его необходимо установить вручную.

В этом руководстве вы будете работать с LocalDB, чтобы базу данных можно было сохранить в папке App_Data в виде файла .mdf . Откройте корневой файлWeb.config и добавьте в коллекцию connectionStrings новый строка подключения, как показано в следующем примере. (Обязательно обновите файлWeb.config в корневой папке проекта. Существует также Web.config файл находится во вложенной папке Представления , которую не нужно обновлять.)

<add name="SchoolContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=ContosoUniversity;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\ContosoUniversity.mdf" providerName="System.Data.SqlClient" />

По умолчанию Entity Framework ищет строка подключения с именем того же, что и DbContext класс (SchoolContext для этого проекта). Добавленный строка подключения указывает базу данных LocalDB с именем ContosoUniversity.mdf, расположенную в папке App_Data. Дополнительные сведения см. в разделе SQL Server строки подключения для веб-приложений ASP.NET.

Указывать строка подключения не нужно. Если вы не предоставляете строка подключения, Entity Framework создаст его для вас, однако база данных может не находиться в папке App_data приложения. Сведения о том, где будет создана база данных, см. в разделе Code First to a New Database.

Коллекция connectionStrings также имеет строка подключения с именем DefaultConnection , которая используется для базы данных членства. В этом руководстве вы не будете использовать базу данных членства. Единственное различие между двумя строками подключения заключается в имени базы данных и значении атрибута name.

Настройка и выполнение первой миграции кода

При первом запуске разработки приложения модель данных часто меняется, и при каждом изменении модели она выходит из-за синхронизации с базой данных. Вы можете настроить Entity Framework для автоматического удаления и повторного создания базы данных при каждом изменении модели данных. Это не проблема на ранних этапах разработки, так как тестовые данные легко создать повторно, но после развертывания в рабочей среде обычно требуется обновить схему базы данных без удаления базы данных. Функция миграции позволяет Code First обновлять базу данных без удаления и повторного создания. В начале цикла разработки нового проекта может потребоваться использовать DropCreateDatabaseIfModelChanges для удаления, повторного создания и повторного заполнения базы данных при каждом изменении модели. Вы готовы к развертыванию приложения и можете преобразовать его в подход миграции. В этом руководстве будут использоваться только миграции. Дополнительные сведения см. в разделе серия Code First Migrations и миграции screencast.

Включение Code First Migrations

  1. В меню Сервис выберите пункт Диспетчер пакетов NuGet , а затем — Консоль диспетчера пакетов.

    Selecting_Package_Manager_Console

  2. В командной строке PM> введите следующую команду:

    enable-migrations -contexttypename SchoolContext
    

    Команда enable-migrations

    Эта команда создает папку Migrations в проекте ContosoUniversity и помещает в нее Configuration.cs файл, который можно изменить для настройки миграции.

    Папка

    Класс Configuration включает метод, Seed который вызывается при создании базы данных и при каждом ее обновлении после изменения модели данных.

    internal sealed class Configuration : DbMigrationsConfiguration<ContosoUniversity.Models.SchoolContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = false;
        }
    
        protected override void Seed(ContosoUniversity.Models.SchoolContext context)
        {
            //  This method will be called after migrating to the latest version.
    
            //  You can use the DbSet<T>.AddOrUpdate() helper extension method 
            //  to avoid creating duplicate seed data. E.g.
            //
            //    context.People.AddOrUpdate(
            //      p => p.FullName,
            //      new Person { FullName = "Andrew Peters" },
            //      new Person { FullName = "Brice Lambson" },
            //      new Person { FullName = "Rowan Miller" }
            //    );
            //
        }
    }
    

    Этот метод предназначен для Seed вставки тестовых данных в базу данных после ее создания или обновления.

Настройка метода seed

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

В более ранних версиях Code First, до выпуска миграций, Seed часто методы вставляли тестовые данные, так как при каждом изменении модели во время разработки базу данных приходилось полностью удалять и повторно создавать с нуля. При использовании Code First Migrations тестовые данные сохраняются после изменений базы данных, поэтому включать тестовые данные в метод Seed обычно не требуется. На самом деле, вы не хотите Seed , чтобы метод вставлял тестовые данные, если вы будете использовать миграции для развертывания базы данных в рабочей среде, так как Seed метод будет выполняться в рабочей среде. В этом случае требуется, Seed чтобы метод вставлял в базу данных только данные, которые нужно вставить в рабочую среду. Например, может потребоваться, чтобы база данных включала в таблицу Department фактические имена отделов, когда приложение станет доступным в рабочей среде.

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

  1. Замените содержимое файла Configuration.cs приведенным ниже кодом, который загрузит тестовые данные в новую базу данных.

    namespace ContosoUniversity.Migrations
    {
       using System;
       using System.Collections.Generic;
       using System.Data.Entity.Migrations;
       using System.Linq;
       using ContosoUniversity.Models;
    
       internal sealed class Configuration : DbMigrationsConfiguration<ContosoUniversity.DAL.SchoolContext>
       {
          public Configuration()
          {
             AutomaticMigrationsEnabled = false;
          }
    
          protected override void Seed(ContosoUniversity.DAL.SchoolContext context)
          {
             var students = new List<Student>
                {
                    new Student { FirstMidName = "Carson",   LastName = "Alexander", 
                        EnrollmentDate = DateTime.Parse("2010-09-01") },
                    new Student { FirstMidName = "Meredith", LastName = "Alonso",    
                        EnrollmentDate = DateTime.Parse("2012-09-01") },
                    new Student { FirstMidName = "Arturo",   LastName = "Anand",     
                        EnrollmentDate = DateTime.Parse("2013-09-01") },
                    new Student { FirstMidName = "Gytis",    LastName = "Barzdukas", 
                        EnrollmentDate = DateTime.Parse("2012-09-01") },
                    new Student { FirstMidName = "Yan",      LastName = "Li",        
                        EnrollmentDate = DateTime.Parse("2012-09-01") },
                    new Student { FirstMidName = "Peggy",    LastName = "Justice",   
                        EnrollmentDate = DateTime.Parse("2011-09-01") },
                    new Student { FirstMidName = "Laura",    LastName = "Norman",    
                        EnrollmentDate = DateTime.Parse("2013-09-01") },
                    new Student { FirstMidName = "Nino",     LastName = "Olivetto",  
                        EnrollmentDate = DateTime.Parse("2005-08-11") }
                };
             students.ForEach(s => context.Students.AddOrUpdate(p => p.LastName, s));
             context.SaveChanges();
    
             var courses = new List<Course>
                {
                    new Course {CourseID = 1050, Title = "Chemistry",      Credits = 3, },
                    new Course {CourseID = 4022, Title = "Microeconomics", Credits = 3, },
                    new Course {CourseID = 4041, Title = "Macroeconomics", Credits = 3, },
                    new Course {CourseID = 1045, Title = "Calculus",       Credits = 4, },
                    new Course {CourseID = 3141, Title = "Trigonometry",   Credits = 4, },
                    new Course {CourseID = 2021, Title = "Composition",    Credits = 3, },
                    new Course {CourseID = 2042, Title = "Literature",     Credits = 4, }
                };
             courses.ForEach(s => context.Courses.AddOrUpdate(p => p.Title, s));
             context.SaveChanges();
    
             var enrollments = new List<Enrollment>
                {
                    new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Alexander").StudentID, 
                        CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID, 
                        Grade = Grade.A 
                    },
                     new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Alexander").StudentID,
                        CourseID = courses.Single(c => c.Title == "Microeconomics" ).CourseID, 
                        Grade = Grade.C 
                     },                            
                     new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Alexander").StudentID,
                        CourseID = courses.Single(c => c.Title == "Macroeconomics" ).CourseID, 
                        Grade = Grade.B
                     },
                     new Enrollment { 
                         StudentID = students.Single(s => s.LastName == "Alonso").StudentID,
                        CourseID = courses.Single(c => c.Title == "Calculus" ).CourseID, 
                        Grade = Grade.B 
                     },
                     new Enrollment { 
                         StudentID = students.Single(s => s.LastName == "Alonso").StudentID,
                        CourseID = courses.Single(c => c.Title == "Trigonometry" ).CourseID, 
                        Grade = Grade.B 
                     },
                     new Enrollment {
                        StudentID = students.Single(s => s.LastName == "Alonso").StudentID,
                        CourseID = courses.Single(c => c.Title == "Composition" ).CourseID, 
                        Grade = Grade.B 
                     },
                     new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Anand").StudentID,
                        CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID
                     },
                     new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Anand").StudentID,
                        CourseID = courses.Single(c => c.Title == "Microeconomics").CourseID,
                        Grade = Grade.B         
                     },
                    new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Barzdukas").StudentID,
                        CourseID = courses.Single(c => c.Title == "Chemistry").CourseID,
                        Grade = Grade.B         
                     },
                     new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Li").StudentID,
                        CourseID = courses.Single(c => c.Title == "Composition").CourseID,
                        Grade = Grade.B         
                     },
                     new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Justice").StudentID,
                        CourseID = courses.Single(c => c.Title == "Literature").CourseID,
                        Grade = Grade.B         
                     }
                };
    
             foreach (Enrollment e in enrollments)
             {
                var enrollmentInDataBase = context.Enrollments.Where(
                    s =>
                         s.Student.StudentID == e.StudentID &&
                         s.Course.CourseID == e.CourseID).SingleOrDefault();
                if (enrollmentInDataBase == null)
                {
                   context.Enrollments.Add(e);
                }
             }
             context.SaveChanges();
          }
       }
    }
    

    Метод Seed принимает объект контекста базы данных в качестве входного параметра, а код в методе использует этот объект для добавления новых сущностей в базу данных. Для каждого типа сущности код создает коллекцию новых сущностей, добавляет их в соответствующее свойство DbSet , а затем сохраняет изменения в базе данных. Не нужно вызывать метод SaveChanges после каждой группы сущностей, как показано здесь, но это поможет найти источник проблемы при возникновении исключения во время записи кода в базу данных.

    Некоторые операторы, которые вставляют данные, используют метод AddOrUpdate для выполнения операции upsert. Seed Так как метод выполняется при каждой миграции, вы не можете просто вставить данные, так как строки, которые вы пытаетесь добавить, уже будут там после первой миграции, в которой создается база данных. Операция upsert предотвращает ошибки, которые могут возникнуть при попытке вставить уже существующую строку, но переопределяет любые изменения данных, которые могли быть внесены во время тестирования приложения. Если тестовые данные находятся в некоторых таблицах, это может не произойти. В некоторых случаях при изменении данных во время тестирования требуется, чтобы изменения сохранялись после обновления базы данных. В этом случае требуется выполнить условную операцию вставки: вставить строку, только если она еще не существует. Метод Seed использует оба подхода.

    Первый параметр, передаваемый методу AddOrUpdate, указывает свойство для проверка, если строка уже существует. Для данных тестового учащегося, которые вы предоставляете, свойство можно использовать для этой цели, LastName так как каждая фамилия в списке уникальна:

    context.Students.AddOrUpdate(p => p.LastName, s)
    

    В этом коде предполагается, что фамилии уникальны. При добавлении учащегося с повторяющимся именем вы получите следующее исключение при следующем выполнении миграции.

    Последовательность содержит несколько элементов

    Дополнительные сведения о методе см. в AddOrUpdate статье Использование метода AddOrUpdate для EF 4.3 в блоге Джули Лерман.

    Код, добавляющий Enrollment сущности AddOrUpdate , не использует метод . Он проверяет, существует ли сущность, и вставляет сущность, если она не существует. Этот подход сохраняет изменения, внесенные в оценку регистрации при выполнении миграции. Код циклически выполняется по каждому члену Enrollmentсписка , и если регистрация не найдена в базе данных, он добавляет регистрацию в базу данных. При первом обновлении базы данных она будет пустой, поэтому она будет добавлять каждую регистрацию.

    foreach (Enrollment e in enrollments)
    {
        var enrollmentInDataBase = context.Enrollments.Where(
            s => s.Student.StudentID == e.Student.StudentID &&
                 s.Course.CourseID == e.Course.CourseID).SingleOrDefault();
        if (enrollmentInDataBase == null)
        {
            context.Enrollments.Add(e);
        }
    }
    

    Сведения об отладке Seed метода и обработке избыточных данных, таких как два учащегося с именем "Александр Карсон", см. в статье Seeding and Debugging Entity Framework (EF) DBs в блоге Рика Андерсона.

  2. Выполните построение проекта.

Создание и выполнение первой миграции

  1. В окне Консоль диспетчера пакетов введите следующие команды:

    add-migration InitialCreate
    update-database
    

    Снимок экрана: окно консоли диспетчера пакетов. Выделены команды add дефис migration (Добавление переноса дефиса) с подчеркиванием Начальное создание и обновление базы данных дефисов.

    Команда add-migration добавляет в папку Migrations файл [DateStamp]_InitialCreate.cs , содержащий код, который создает базу данных. Первый параметр (InitialCreate) используется для имени файла и может быть любым; обычно вы выбираете слово или фразу, которые обобщают действия, выполняемые в миграции. Например, последнюю миграцию можно назвать "AddDepartmentTable".

    Папка Migrations с начальной миграцией

    Метод UpInitialCreate класса создает таблицы базы данных, соответствующие наборам сущностей модели данных, и Down метод удаляет их. Функция миграций вызывает метод Up, чтобы реализовать изменения модели данных для миграции. При вводе команды для отката обновления функция миграций вызывает метод Down. В следующем коде показано содержимое InitialCreate файла:

    namespace ContosoUniversity.Migrations
    {
        using System;
        using System.Data.Entity.Migrations;
        
        public partial class InitialCreate : DbMigration
        {
            public override void Up()
            {
                CreateTable(
                    "dbo.Student",
                    c => new
                        {
                            StudentID = c.Int(nullable: false, identity: true),
                            LastName = c.String(),
                            FirstMidName = c.String(),
                            EnrollmentDate = c.DateTime(nullable: false),
                        })
                    .PrimaryKey(t => t.StudentID);
                
                CreateTable(
                    "dbo.Enrollment",
                    c => new
                        {
                            EnrollmentID = c.Int(nullable: false, identity: true),
                            CourseID = c.Int(nullable: false),
                            StudentID = c.Int(nullable: false),
                            Grade = c.Int(),
                        })
                    .PrimaryKey(t => t.EnrollmentID)
                    .ForeignKey("dbo.Course", t => t.CourseID, cascadeDelete: true)
                    .ForeignKey("dbo.Student", t => t.StudentID, cascadeDelete: true)
                    .Index(t => t.CourseID)
                    .Index(t => t.StudentID);
                
                CreateTable(
                    "dbo.Course",
                    c => new
                        {
                            CourseID = c.Int(nullable: false),
                            Title = c.String(),
                            Credits = c.Int(nullable: false),
                        })
                    .PrimaryKey(t => t.CourseID);
                
            }
            
            public override void Down()
            {
                DropIndex("dbo.Enrollment", new[] { "StudentID" });
                DropIndex("dbo.Enrollment", new[] { "CourseID" });
                DropForeignKey("dbo.Enrollment", "StudentID", "dbo.Student");
                DropForeignKey("dbo.Enrollment", "CourseID", "dbo.Course");
                DropTable("dbo.Course");
                DropTable("dbo.Enrollment");
                DropTable("dbo.Student");
            }
        }
    }
    

    Команда update-database запускает Up метод для создания базы данных, а затем — Seed метод для заполнения базы данных.

Теперь для вашей модели данных создана база данных SQL Server. Имя базы данных — ContosoUniversity, а файл .mdf находится в папке App_Data проекта, так как это указано в строка подключения.

Для просмотра базы данных в Visual Studio можно использовать server Обозреватель или SQL Server обозреватель объектов (SSOX). В этом руководстве используется серверная Обозреватель. В Visual Studio Express 2012 для Web Обозреватель сервера называется Обозреватель базы данных.

  1. В меню Вид выберите сервер Обозреватель.

  2. Щелкните значок Добавить подключение .

    Снимок экрана: окно Обозреватель базы данных. Выделен значок Добавить подключение.

  3. Если появится запрос в диалоговом окне Выбор источника данных, щелкните Microsoft SQL Server, а затем нажмите кнопку Продолжить.

    Снимок экрана: диалоговое окно

  4. В диалоговом окне Добавление подключения введите (localdb)\v11.0 в поле Имя сервера. В разделе Выберите или введите имя базы данных выберите ContosoUniversity.

    Снимок экрана: диалоговое окно

  5. Нажмите кнопку ОК.

  6. Разверните узел SchoolContext , а затем — Таблицы.

    Снимок экрана: страница Обозреватель сервера. Вкладки School Context (Контекст учебного заведения) и Tables (Таблицы) развернуты.

  7. Щелкните правой кнопкой мыши таблицу Student и выберите команду Показать данные таблицы , чтобы просмотреть созданные столбцы и строки, которые были вставлены в таблицу.

    Таблица student

Создание контроллера и представлений учащихся

Следующим шагом является создание ASP.NET контроллера MVC и представлений в приложении, который может работать с одной из этих таблиц.

  1. Чтобы создать контроллер, щелкните правой Student кнопкой мыши папку Контроллеры в Обозреватель решений, выберите Добавить, а затем — Контроллер. В диалоговом окне Добавление контроллера выберите следующие параметры и нажмите кнопку Добавить:

    • Имя контроллера: StudentController.

    • Шаблон: контроллер MVC с действиями чтения и записи и представлениями с использованием Entity Framework.

    • Класс модели: Student (ContosoUniversity.Models). (Если вы не видите этот параметр в раскрывающемся списке, выполните сборку проекта и повторите попытку.)

    • Класс контекста данных: SchoolContext (ContosoUniversity.Models).

    • Представления: Razor (CSHTML). (Значение по умолчанию.)

      Add_Controller_dialog_box_for_Student_controller

  2. Visual Studio открывает файл Controllers\StudentController.cs . Вы увидите, что создана переменная класса, которая создает экземпляр объекта контекста базы данных:

    private SchoolContext db = new SchoolContext();
    

    Метод Index действия получает список учащихся из набора сущностей Students , считывая Students свойство экземпляра контекста базы данных:

    public ViewResult Index()
    {
        return View(db.Students.ToList());
    }
    

    Представление Student\Index.cshtml отображает этот список в таблице:

    <table>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.LastName)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.FirstMidName)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.EnrollmentDate)
            </th>
            <th></th>
        </tr>
    
    @foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.LastName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.FirstMidName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.EnrollmentDate)
            </td>
            <td>
                @Html.ActionLink("Edit", "Edit", new { id=item.StudentID }) |
                @Html.ActionLink("Details", "Details", new { id=item.StudentID }) |
                @Html.ActionLink("Delete", "Delete", new { id=item.StudentID })
            </td>
        </tr>
    }
    
  3. Нажмите клавиши CTRL+F5, чтобы запустить проект.

    Перейдите на вкладку Учащиеся , чтобы просмотреть тестовые данные, вставленные методом Seed .

    Страница индекса учащихся

Соглашения

Объем кода, который необходимо написать, чтобы платформа Entity Framework могла создать полную базу данных, минимальный из-за использования соглашений или допущений, которые делает Entity Framework. Некоторые из них уже отмечены:

  • В качестве имен таблиц используются во множественном числе имена классов сущностей.
  • В качестве имен столбцов используются имена свойств сущностей.
  • Свойства сущности с именем ID или classnameID распознаются как свойства первичного ключа.

Вы видели, что соглашения можно переопределить (например, вы указали, что имена таблиц не должны быть во множественном числе), и вы узнаете больше о соглашениях и их переопределении в учебнике Создание более сложной модели данных далее в этой серии. Дополнительные сведения см. в разделе Соглашения Code First.

Сводка

Теперь вы создали простое приложение, которое использует Entity Framework и SQL Server Express для хранения и отображения данных. В следующем руководстве вы узнаете, как выполнять базовые операции CRUD (создание, чтение, обновление, удаление). Вы можете оставить отзыв в нижней части этой страницы. Сообщите нам, как вам понравилась эта часть руководства и как мы можем улучшить ее.

Ссылки на другие ресурсы Entity Framework можно найти в ASP.NET схеме содержимого доступа к данным.