Руководство. Дополнительные сведения о сценариях EF для веб-приложения MVC 5

В предыдущем руководстве вы реализовали наследование таблицы на иерархию. В этом руководстве представлено несколько разделов, которые полезно учитывать, когда вы выходите за рамки разработки ASP.NET веб-приложений, использующих Entity Framework Code First. В первых нескольких разделах содержатся пошаговые инструкции по использованию кода и использованию Visual Studio для выполнения задач. В следующих разделах представлено несколько разделов с краткими вводные сведения, за которыми следуют ссылки на ресурсы для получения дополнительных сведений.

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

Update_Course_Credits_initial_page

В этом учебнике рассмотрены следующие задачи.

  • Выполнение прямых SQL-запросов
  • Выполнение запросов без отслеживания
  • Изучение SQL-запросов, отправленных в базу данных

Вы также узнаете о следующем:

  • Создание слоя абстракции
  • Классы прокси-сервера
  • Автоматическое обнаружение изменений
  • Автоматическая проверка
  • Entity Framework Power Tools
  • Исходный код Entity Framework

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

Выполнение прямых SQL-запросов

API Entity Framework Code First включает методы, позволяющие передавать команды SQL непосредственно в базу данных. Вам доступны следующие варианты:

  • Используйте метод DbSet.SqlQuery для запросов, возвращающих типы сущностей. Возвращаемые объекты должны иметь тип, ожидаемый DbSet объектом, и они автоматически отслеживаются контекстом базы данных, если вы не отключите отслеживание. (См. следующий раздел о методе AsNoTracking .)
  • Используйте метод Database.SqlQuery для запросов, возвращающих типы, которые не являются сущностями. Возвращаемые данные не отслеживаются контекстом базы данных, даже если вы используете этот метод для извлечения типов сущностей.
  • Используйте Database.ExecuteSqlCommand для команд, не относящихся к запросам.

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

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

Вызов запроса, возвращающего сущности

Класс DbSet<TEntity> предоставляет метод, который можно использовать для выполнения запроса, возвращающего сущность типа TEntity. Чтобы увидеть, как это работает, измените код в Details методе контроллера Department .

В файле DepartmentController.cs в методе Details замените db.Departments.FindAsync вызов метода вызовом db.Departments.SqlQuery метода, как показано в следующем выделенном коде:

public async Task<ActionResult> Details(int? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }

    // Commenting out original code to show how to use a raw SQL query.
    //Department department = await db.Departments.FindAsync(id);

    // Create and execute raw SQL query.
    string query = "SELECT * FROM Department WHERE DepartmentID = @p0";
    Department department = await db.Departments.SqlQuery(query, id).SingleOrDefaultAsync();
    
    if (department == null)
    {
        return HttpNotFound();
    }
    return View(department);
}

Чтобы убедиться, что новый код работает правильно, выберите вкладку Departments (Кафедры) и щелкните Details (Сведения) для одной из кафедр. Убедитесь, что все данные отображаются должным образом.

Вызов запроса, возвращающего другие типы объектов

Ранее вы создали таблицу статистики учащихся на странице сведений, в которой было показано число учащихся на каждую дату регистрации. Код, который делает это в Файле HomeController.cs , использует LINQ:

var data = from student in db.Students
           group student by student.EnrollmentDate into dateGroup
           select new EnrollmentDateGroup()
           {
               EnrollmentDate = dateGroup.Key,
               StudentCount = dateGroup.Count()
           };

Предположим, вы хотите написать код, который извлекает эти данные непосредственно в SQL, а не с помощью LINQ. Для этого необходимо выполнить запрос, который возвращает не объекты сущностей, а это означает, что необходимо использовать метод Database.SqlQuery .

В файле HomeController.cs замените инструкцию LINQ в About методе на инструкцию SQL, как показано в следующем выделенном коде:

public ActionResult About()
{
    // Commenting out LINQ to show how to do the same thing in SQL.
    //IQueryable<EnrollmentDateGroup> = from student in db.Students
    //           group student by student.EnrollmentDate into dateGroup
    //           select new EnrollmentDateGroup()
    //           {
    //               EnrollmentDate = dateGroup.Key,
    //               StudentCount = dateGroup.Count()
    //           };

    // SQL version of the above LINQ code.
    string query = "SELECT EnrollmentDate, COUNT(*) AS StudentCount "
        + "FROM Person "
        + "WHERE Discriminator = 'Student' "
        + "GROUP BY EnrollmentDate";
    IEnumerable<EnrollmentDateGroup> data = db.Database.SqlQuery<EnrollmentDateGroup>(query);

    return View(data.ToList());
}

Запустите страницу О программе. Убедитесь, что отображаются те же данные, что и раньше.

Вызов запроса на обновление

Предположим, что администраторы Университета Contoso хотят иметь возможность выполнять массовые изменения в базе данных, например изменять количество кредитов для каждого курса. Поскольку в университете ведется множество курсов, будет неэффективно извлекать их в виде сущностей и изменять по отдельности. В этом разделе вы реализуете веб-страницу, которая позволяет пользователю указать фактор, с помощью которого можно изменить количество кредитов для всех курсов, и внести изменения, выполнив инструкцию SQL UPDATE .

В Файле CourseController.cs добавьте UpdateCourseCredits методы для HttpGet и HttpPost:

public ActionResult UpdateCourseCredits()
{
    return View();
}

[HttpPost]
public ActionResult UpdateCourseCredits(int? multiplier)
{
    if (multiplier != null)
    {
        ViewBag.RowsAffected = db.Database.ExecuteSqlCommand("UPDATE Course SET Credits = Credits * {0}", multiplier);
    }
    return View();
}

Когда контроллер обрабатывает запрос, в переменной HttpGet ничего не возвращается, а в ViewBag.RowsAffected представлении отображается пустое текстовое поле и кнопка отправки.

При нажатии HttpPost кнопки Обновить вызывается метод и multiplier содержит значение, введенное в текстовом поле. Затем код выполняет SQL, который обновляет курсы и возвращает количество затронутых строк в представление в переменной ViewBag.RowsAffected . Когда представление получает значение в этой переменной, оно отображает количество обновленных строк вместо текстового поля и кнопки отправки.

В файле CourseController.cs щелкните правой кнопкой мыши один из UpdateCourseCredits методов и выберите команду Добавить представление. Откроется диалоговое окно Добавление представления . Оставьте значения по умолчанию и нажмите кнопку Добавить.

В файле Views\Course\UpdateCourseCredits.cshtml замените код шаблона следующим кодом:

@model ContosoUniversity.Models.Course

@{
    ViewBag.Title = "UpdateCourseCredits";
}

<h2>Update Course Credits</h2>

@if (ViewBag.RowsAffected == null)
{
    using (Html.BeginForm())
    {
        <p>
            Enter a number to multiply every course's credits by: @Html.TextBox("multiplier")
        </p>
        <p>
            <input type="submit" value="Update" />
        </p>
    }
}
@if (ViewBag.RowsAffected != null)
{
    <p>
        Number of rows updated: @ViewBag.RowsAffected
    </p>
}
<div>
    @Html.ActionLink("Back to List", "Index")
</div>

Выполните метод UpdateCourseCredits, выбрав вкладку Courses (Курсы), а затем добавив "/UpdateCourseCredits" в конец URL-адреса в адресной строке браузера (например, http://localhost:50205/Course/UpdateCourseCredits). Введите число в текстовое поле:

Update_Course_Credits_initial_page_with_2_entered

Нажмите кнопку Обновить. Вы увидите количество затронутых строк.

Нажмите кнопку Back to List (Вернуться к списку), чтобы просмотреть список курсов с измененным числом зачетных баллов.

Дополнительные сведения о необработанных SQL-запросах см. в статье Необработанные SQL-запросы на сайте MSDN.

Отключение отслеживания запросов

Когда контекст базы данных извлекает строки таблицы и создает представляющие их объекты сущностей, по умолчанию отслеживается состояние синхронизации сущностей в памяти с содержимым базы данных. При обновлении сущности данные в памяти выступают в роли кэша. В веб-приложении такое кэширование часто не нужно, поскольку экземпляры контекста, как правило, существуют недолго (для каждого запроса создается и ликвидируется собственный экземпляр), и контекст, считывающий сущность, как правило, ликвидируется до того, как сущность будет использована снова.

Вы можете отключить отслеживание объектов сущностей в памяти с помощью метода AsNoTracking . Как правило, это требуется в следующих сценариях:

  • Запрос извлекает такой большой объем данных, что отключение отслеживания может заметно повысить производительность.
  • Вы хотите прикрепить сущность, чтобы обновить ее, но ранее вы получили ту же сущность для другой цели. Поскольку сущность уже отслеживается контекстом базы данных, присоединить сущность, которую требуется изменить, нельзя. Один из способов справиться с этой ситуацией — использовать AsNoTracking параметр с предыдущим запросом.

Пример использования метода AsNoTracking см. в предыдущей версии этого руководства. Эта версия учебника не устанавливает флаг Modified для сущности, созданной связывателем модели в методе Edit, поэтому ему не требуется AsNoTracking.

Изучение SQL, отправляемого в базу данных

В некоторых случаях полезно иметь возможность просмотреть фактические SQL-запросы, отправляемые в базу данных. В предыдущем руководстве вы узнали, как это сделать в коде перехватчика; Теперь вы увидите несколько способов сделать это без написания кода перехватчика. Чтобы попробовать это, вы посмотрите на простой запрос, а затем посмотрите, что с ним происходит при добавлении параметров, таких как неотложная загрузка, фильтрация и сортировка.

В controllers/CourseController замените Index метод следующим кодом, чтобы временно остановить неотложную загрузку:

public ActionResult Index()
{
    var courses = db.Courses;
    var sql = courses.ToString();
    return View(courses.ToList());
}

Теперь установите точку останова для return оператора (F9 с курсором на этой строке). Нажмите клавишу F5 , чтобы запустить проект в режиме отладки, и выберите страницу Индекс курса. Когда код достигает точки останова, изучите переменную sql . Вы увидите запрос, отправляемый в SQL Server. Это простое Select утверждение.

{SELECT 
[Extent1].[CourseID] AS [CourseID], 
[Extent1].[Title] AS [Title], 
[Extent1].[Credits] AS [Credits], 
[Extent1].[DepartmentID] AS [DepartmentID]
FROM [Course] AS [Extent1]}

Щелкните лупу, чтобы увидеть запрос в визуализаторе текста.

Снимок экрана: контроллер курса с выделенной строкой кода. На другом снимке экрана показан открытый визуализатор текста, а в поле Значение обведена лупа красным цветом.

Теперь вы добавите раскрывающийся список на страницу "Индекс курсов", чтобы пользователи могли выполнять фильтрацию по определенному отделу. Вы отсортируете курсы по названию и укажите неотложную загрузку для свойства навигации Department .

В файле CourseController.cs замените Index метод следующим кодом:

public ActionResult Index(int? SelectedDepartment)
{
    var departments = db.Departments.OrderBy(q => q.Name).ToList();
    ViewBag.SelectedDepartment = new SelectList(departments, "DepartmentID", "Name", SelectedDepartment);
    int departmentID = SelectedDepartment.GetValueOrDefault();

    IQueryable<Course> courses = db.Courses
        .Where(c => !SelectedDepartment.HasValue || c.DepartmentID == departmentID)
        .OrderBy(d => d.CourseID)
        .Include(d => d.Department);
    var sql = courses.ToString();
    return View(courses.ToList());
}

Восстановите точку останова в инструкции return .

Метод получает выбранное значение раскрывающегося списка в параметре SelectedDepartment . Если ничего не выбрано, этот параметр будет иметь значение NULL.

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

Get Для метода репозитория Course код задает выражение фильтра, порядок сортировки и неотложную загрузку для свойства навигацииDepartment. Выражение фильтра всегда возвращает значение true , SelectedDepartment если в раскрывающемся списке ничего не выбрано (то есть имеет значение NULL).

В views\Course\Index.cshtml непосредственно перед открывающим table тегом добавьте следующий код, чтобы создать раскрывающийся список и кнопку отправки:

@using (Html.BeginForm())
{
    <p>Select Department: @Html.DropDownList("SelectedDepartment","All")   
    <input type="submit" value="Filter" /></p>
}

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

На этот раз первой точкой останова будет запрос отделов для раскрывающегося списка. Пропустите это и просмотрите query переменную, когда код в следующий раз достигнет точки останова Course , чтобы увидеть, как теперь выглядит запрос. Вы увидите примерно следующее:

SELECT 
    [Project1].[CourseID] AS [CourseID], 
    [Project1].[Title] AS [Title], 
    [Project1].[Credits] AS [Credits], 
    [Project1].[DepartmentID] AS [DepartmentID], 
    [Project1].[DepartmentID1] AS [DepartmentID1], 
    [Project1].[Name] AS [Name], 
    [Project1].[Budget] AS [Budget], 
    [Project1].[StartDate] AS [StartDate], 
    [Project1].[InstructorID] AS [InstructorID], 
    [Project1].[RowVersion] AS [RowVersion]
    FROM ( SELECT 
        [Extent1].[CourseID] AS [CourseID], 
        [Extent1].[Title] AS [Title], 
        [Extent1].[Credits] AS [Credits], 
        [Extent1].[DepartmentID] AS [DepartmentID], 
        [Extent2].[DepartmentID] AS [DepartmentID1], 
        [Extent2].[Name] AS [Name], 
        [Extent2].[Budget] AS [Budget], 
        [Extent2].[StartDate] AS [StartDate], 
        [Extent2].[InstructorID] AS [InstructorID], 
        [Extent2].[RowVersion] AS [RowVersion]
        FROM  [dbo].[Course] AS [Extent1]
        INNER JOIN [dbo].[Department] AS [Extent2] ON [Extent1].[DepartmentID] = [Extent2].[DepartmentID]
        WHERE @p__linq__0 IS NULL OR [Extent1].[DepartmentID] = @p__linq__1
    )  AS [Project1]
    ORDER BY [Project1].[CourseID] ASC

Вы увидите, что теперь запрос является запросом JOIN , который загружает Department данные вместе с данными Course , и что он включает WHERE предложение .

var sql = courses.ToString() Удалите строку.

Создание уровня абстракции

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

  • Класс контекста EF сам по себе изолирует код от кода хранилища данных.
  • Класс контекста EF может выступать в качестве класса единиц работы для обновления базы данных, которые выполняются с помощью EF.
  • Функции, представленные в Entity Framework 6, упрощают реализацию TDD без написания кода репозитория.

Дополнительные сведения о том, как реализовать шаблоны репозитория и единицы работы, см. в этой серии руководств в версии Entity Framework 5. Сведения о способах реализации TDD в Entity Framework 6 см. в следующих ресурсах:

Прокси-классы

Когда Платформа Entity Framework создает экземпляры сущности (например, при выполнении запроса), она часто создает их как экземпляры динамически созданного производного типа, который выступает в качестве прокси-сервера для сущности. Например, см. следующие два образа отладчика. На первом изображении student видно, что переменная является ожидаемым Student типом сразу после создания экземпляра сущности. На втором изображении после использования EF для чтения сущности учащегося из базы данных вы увидите прокси-класс.

Класс перед прокси-сервером

Класс после прокси-сервера

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

В большинстве случаев вам не нужно знать о таком использовании прокси-серверов, но существуют исключения:

  • В некоторых сценариях может потребоваться запретить Entity Framework создавать экземпляры прокси-сервера. Например, при сериализации сущностей обычно нужны классы POCO, а не прокси-классы. Одним из способов избежать проблем сериализации является сериализация объектов передачи данных (DTO) вместо объектов сущностей, как показано в руководстве Использование веб-API с Entity Framework . Другой способ — отключить создание прокси-сервера.
  • При создании экземпляра класса сущности new с помощью оператора вы не получаете экземпляр прокси-сервера. Это означает, что вы не получаете такие функции, как отложенная загрузка и автоматическое отслеживание изменений. Обычно это нормально; Обычно отложенная загрузка не требуется, так как вы создаете новую сущность, которая отсутствует в базе данных, и обычно не требуется отслеживание изменений, если вы явно помечаете сущность как Added. Однако если требуется отложенная загрузка и отслеживание изменений, можно создать новые экземпляры сущностей с прокси-серверами с помощью метода DbSetCreate класса .
  • Может потребоваться получить фактический тип сущности из прокси-типа. Чтобы получить фактический тип сущности ObjectContext экземпляра прокси-типа, можно использовать метод GetObjectType класса .

Дополнительные сведения см. в статье Работа с прокси-серверами на сайте MSDN.

Автоматическое обнаружение изменений

Платформа Entity Framework определяет, как была изменена сущность (и, соответственно, какие обновления требуется отправить в базу данных), сравнивая текущие значения сущности с исходными. Исходные значения сохраняются при запросе или присоединении сущности. Ниже перечислены некоторые из методов, которые приводят к автоматическому обнаружению изменений:

  • DbSet.Find
  • DbSet.Local
  • DbSet.Remove
  • DbSet.Add
  • DbSet.Attach
  • DbContext.SaveChanges
  • DbContext.GetValidationErrors
  • DbContext.Entry
  • DbChangeTracker.Entries

Если вы отслеживаете большое количество сущностей и вызываете один из этих методов много раз в цикле, вы можете получить значительное повышение производительности, временно отключив автоматическое обнаружение изменений с помощью свойства AutoDetectChangesEnabled . Дополнительные сведения см. в статье Автоматическое обнаружение изменений на сайте MSDN.

Автоматическая проверка

При вызове SaveChanges метода Entity Framework по умолчанию проверяет данные во всех свойствах всех измененных сущностей перед обновлением базы данных. Если вы обновили большое количество сущностей и уже проверили данные, эта работа не нужна, и вы можете сделать процесс сохранения изменений занимает меньше времени, временно отключив проверку. Это можно сделать с помощью свойства ValidateOnSaveEnabled . Дополнительные сведения см. в статье Проверка на сайте MSDN.

Entity Framework Power Tools

Entity Framework Power Tools — это надстройка Visual Studio, которая использовалась для создания схем модели данных, показанных в этих руководствах. Эти средства также могут выполнять другие функции, например создавать классы сущностей на основе таблиц в существующей базе данных, чтобы можно было использовать базу данных с Code First. После установки средств в контекстных меню появятся некоторые дополнительные параметры. Например, щелкнув правой кнопкой мыши класс контекста в Обозреватель решений, вы увидите параметр и Entity Framework. Это дает возможность создать схему. При использовании Code First вы не можете изменить модель данных на схеме, но вы можете перемещать все вокруг, чтобы упростить понимание.

Схема EF

Исходный код Entity Framework

Исходный код для Entity Framework 6 доступен на сайте GitHub. Вы можете сообщить об ошибках и внести свои собственные улучшения в исходный код EF.

Хотя исходный код открыт, Платформа Entity Framework полностью поддерживается как продукт Майкрософт. Команда Microsoft Entity Framework контролирует предложения участников, принимает их и тестирует любые изменения кода, чтобы обеспечить максимальное качество каждого выпуска.

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

  • Том Дайкстра (Tom Dykstra) написал исходную версию этого руководства, соавтор обновления EF 5 и обновление EF 6. Том является старшим автором программ в группе содержимого веб-платформы и инструментов Майкрософт.
  • Рик Андерсон (twitter @RickAndMSFT) выполнил большую часть работы, обновив учебник для EF 5 и MVC 4, и в соавторстве с обновлением EF 6. Рик ( Rick) — старший писатель по программированию в корпорации Майкрософт, который специализируется на Azure и MVC.
  • Роуэн Миллер и другие члены команды Entity Framework помогли с проверками кода и помогли отладить многие проблемы с миграцией, которые возникли во время обновления учебника для EF 5 и EF 6.

Устранение распространенных ошибок

Не удается создать или теневое копирование

Сообщение об ошибке:

Не удается создать или теневое копирование filename<>, если этот файл уже существует.

Решение

Подождите несколько секунд и обновите страницу.

Update-Database не распознано

Сообщение об ошибке Update-Database (из команды в PMC):

Термин "Update-Database" не распознается как имя командлета, функции, файла скрипта или действующей программы. Проверьте правильность написания имени, а если включен путь, то проверьте правильность пути и повторите попытку.

Решение

Закройте Visual Studio. Повторно откройте проект и повторите попытку.

Проверка завершена с ошибкой

Сообщение об ошибке Update-Database (из команды в PMC):

Сбой проверки для одной или нескольких сущностей. Дополнительные сведения см. в разделе Свойство EntityValidationErrors.

Решение

Одной из причин этой проблемы являются ошибки проверки при выполнении Seed метода. Советы по отладке метода см. в Seed разделе Начальное и отладка баз данных Entity Framework (EF).

Ошибка HTTP 500.19

Сообщение об ошибке:

Ошибка HTTP 500.19 — внутренняя ошибка сервера. Доступ к запрошенной странице невозможен, так как связанные данные конфигурации для страницы недопустимы.

Решение

Один из способов получить эту ошибку — наличие нескольких копий решения, каждая из которых использует один и тот же номер порта. Обычно эту проблему можно решить, выйдя из всех экземпляров Visual Studio, а затем перезапустив проект, над которым вы работаете. Если это не сработает, попробуйте изменить номер порта. Щелкните правой кнопкой мыши файл проекта и выберите свойства. Перейдите на вкладку Веб и измените номер порта в текстовом поле Url-адрес проекта .

Ошибка при обнаружении экземпляра SQL Server

Сообщение об ошибке:

При подключении к SQL Server произошла ошибка, связанная с сетью или с определенным экземпляром. Сервер не найден или недоступен. Проверьте правильность имени экземпляра и настройку сервера SQL Server для удаленных подключений. (поставщик: сетевые интерфейсы SQL, ошибка: 26 — ошибка при обнаружении указанного сервера или экземпляра)

Решение

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

Получите код

Скачать завершенный проект

Дополнительные ресурсы

Дополнительные сведения о работе с данными с помощью Entity Framework см. на странице документации по EF на сайте MSDN и ASP.NET доступ к данным — рекомендуемые ресурсы.

Дополнительные сведения о развертывании веб-приложения после его создания см. в разделе ASP.NET веб-развертывание — рекомендуемые ресурсы в библиотека MSDN.

Сведения о других темах, связанных с MVC, таких как проверка подлинности и авторизация, см. в разделе ASP.NET MVC — рекомендуемые ресурсы.

Дальнейшие действия

В этом учебнике рассмотрены следующие задачи.

  • Выполнение прямых SQL-запросов
  • Запросы без отслеживания
  • Просмотр запросов SQL, отправленных в базу данных

Вы также узнали о следующем:

  • Создание слоя абстракции
  • Классы прокси-сервера
  • Автоматическое обнаружение изменений
  • Автоматическая проверка
  • Entity Framework Power Tools
  • Исходный код Entity Framework

На этом показано, как использовать Entity Framework в приложении MVC ASP.NET. Если вы хотите узнать о EF Database First, см. серию учебников DB First.