Максимизация производительности с помощью Entity Framework 4.0 в веб-приложении ASP.NET 4

Том Дайкстра

Эта серия руководств основана на веб-приложении Университета Contoso, созданном начало работы с серией учебников По Entity Framework 4.0. Если вы не выполнили предыдущие руководства, в качестве отправной точки для этого руководства можно скачать созданное приложение . Вы также можете скачать приложение , созданное в полной серии учебников. Если у вас есть вопросы по этим руководствам, вы можете опубликовать их на форуме ASP.NET Entity Framework.

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

Сведения, представленные в следующих разделах, скорее всего, будут полезны в самых разных сценариях:

  • Эффективная загрузка связанных данных.
  • Управление состоянием представления.

Сведения, представленные в следующих разделах, могут оказаться полезными, если у вас есть отдельные запросы, которые представляют проблемы с производительностью:

  • NoTracking Используйте параметр слияния.
  • Предварительная компиляция запросов LINQ.
  • Изучите команды запроса, отправленные в базу данных.

Сведения, представленные в следующем разделе, потенциально полезны для приложений с очень большими моделями данных:

  • Предварительное создание представлений.

Примечание

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

В этом разделе основное внимание уделяется способам потенциального повышения производительности entity Framework в ASP.NET. Приведенные здесь рекомендации полезны, если вы определили, что доступ к данным является одним из узких мест производительности в приложении. За исключением отмеченного, описанные здесь методы не следует рассматривать как "лучшие методики" в целом— многие из них подходят только в исключительных ситуациях или для решения весьма конкретных узких мест производительности.

Чтобы приступить к работе с этим руководством, запустите Visual Studio и откройте веб-приложение Университета Contoso, с которым вы работали в предыдущем руководстве.

Платформа Entity Framework может загружать связанные данные в свойства навигации сущности несколькими способами.

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

    Image05

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

Изображение07

  • Явная загрузка. Это похоже на отложенную загрузку, за исключением того, что вы явным образом извлекаете связанные данные в коде; Это не происходит автоматически при доступе к свойству навигации. Связанные данные загружают вручную с помощью Load метода свойства навигации для коллекций или используют Load метод ссылочного свойства для свойств, которые содержат один объект. (Например, вызывается PersonReference.Load метод для загрузки свойства навигации Person сущности Department .)

    Image06

Так как они не получают значения свойств сразу, отложенная загрузка и явная загрузка также называются отложенной загрузкой.

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

this.ContextOptions.LazyLoadingEnabled = true;

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

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

Кроме того, элемент управления для исходящего трафика данных может использовать объект сущности после удаления контекста объекта. В этом случае попытка отложенной загрузки свойства навигации завершится ошибкой. Отображается ясное сообщение об ошибке: "The ObjectContext instance has been disposed and can no longer be used for operations that require a connection."

Элемент EntityDataSource управления по умолчанию отключает отложенную загрузку. ObjectDataSource Для элемента управления, который вы используете для текущего руководства (или если вы обращаетесь к контексту объекта из кода страницы), есть несколько способов отключить отложенную загрузку по умолчанию. Его можно отключить при создании экземпляра контекста объекта. Например, можно добавить следующую строку в метод конструктора SchoolRepository класса :

context.ContextOptions.LazyLoadingEnabled = false;

Для приложения Contoso University вы автоматически отключаете отложенную загрузку контекста объекта, чтобы не задавать это свойство при создании экземпляра контекста.

Откройте модель данных SchoolModel.edmx , щелкните область конструктора, а затем в области свойств установите для свойства Отложенная загрузка включено значение False. Сохраните и закройте модель данных.

Image04

Управление состоянием представления

Чтобы обеспечить функциональность обновления, ASP.NET веб-страница должна хранить исходные значения свойств сущности при отрисовке страницы. Во время обратной обработки элемент управления может повторно создать исходное состояние сущности и вызвать метод сущности Attach перед применением изменений и вызовом SaveChanges метода . По умолчанию ASP.NET Web Forms элементы управления данными используют состояние представления для хранения исходных значений. Однако состояние представления может повлиять на производительность, так как оно хранится в скрытых полях, которые могут значительно увеличить размер страницы, отправляемой в браузер и из него.

Методы управления состоянием представления или альтернативные варианты, такие как состояние сеанса, не являются уникальными для Entity Framework, поэтому в этом руководстве подробно не рассматривается этот раздел. Дополнительные сведения см. по ссылкам в конце учебника.

Однако версия 4 ASP.NET предоставляет новый способ работы с состоянием представления, который должен знать каждый ASP.NET разработчик веб-формы приложений: ViewStateMode свойство . Это новое свойство можно задать на уровне страницы или элемента управления и позволяет отключить состояние просмотра по умолчанию для страницы и включить его только для элементов управления, которым оно нужно.

Для приложений, где важна производительность, рекомендуется всегда отключать состояние просмотра на уровне страницы и включать его только для элементов управления, которым оно требуется. Размер состояния представления на страницах Университета Contoso не будет существенно уменьшен этим методом, но чтобы увидеть, как это работает, вы сделаете это для страницы Instructors.aspx . Эта страница содержит множество элементов управления, включая Label элемент управления с отключенным состоянием просмотра. Ни один из элементов управления на этой странице не должен включать состояние просмотра. (Свойство DataKeyNamesGridView элемента управления указывает состояние, которое должно поддерживаться между обратными передачами, но эти значения хранятся в состоянии элемента управления, на которое свойство не влияет ViewStateMode .)

В настоящее время директива Page и Label разметка элемента управления похожи на следующий пример:

<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true"
    CodeBehind="Instructors.aspx.cs" Inherits="ContosoUniversity.Instructors" %>
    ...
    <asp:Label ID="ErrorMessageLabel" runat="server" Text="" Visible="false" ViewStateMode="Disabled"></asp:Label> 
    ...

Внесите следующие изменения:

  • Добавьте ViewStateMode="Disabled" в директиву Page .
  • Удалить ViewStateMode="Disabled" из Label элемента управления .

Разметка теперь похожа на следующий пример:

<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true"
    CodeBehind="Instructors.aspx.cs" Inherits="ContosoUniversity.Instructors" 
    ViewStateMode="Disabled" %>
    ...
    <asp:Label ID="ErrorMessageLabel" runat="server" Text="" Visible="false"></asp:Label> 
    ...

Состояние просмотра теперь отключено для всех элементов управления. Если позже вы добавите элемент управления, который должен использовать состояние представления, достаточно включить ViewStateMode="Enabled" атрибут для этого элемента управления.

Использование параметра noTracking Merge

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

В Entity Framework можно указать, отслеживает ли контекст объекта объекты сущности, задав параметр слияния. Параметр слияния можно задать для отдельных запросов или для наборов сущностей. Если вы задали его для набора сущностей, это означает, что вы устанавливаете параметр слияния по умолчанию для всех запросов, созданных для этого набора сущностей.

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

В папке DAL откройте файл SchoolRepository.cs и добавьте метод конструктора, который задает параметр слияния для наборов сущностей, к которым обращается репозиторий:

public SchoolRepository()
{
    context.Departments.MergeOption = MergeOption.NoTracking;
    context.InstructorNames.MergeOption = MergeOption.NoTracking;
    context.OfficeAssignments.MergeOption = MergeOption.NoTracking;
}

Предварительная компиляция запросов LINQ

Когда Платформа Entity Framework впервые выполняет запрос Entity SQL в течение срока действия данного ObjectContext экземпляра, компиляция запроса занимает некоторое время. Результат компиляции кэшируется, что означает, что последующие выполнения запроса выполняются гораздо быстрее. Запросы LINQ следуют аналогичному шаблону, за исключением того, что часть работы, необходимой для компиляции запроса, выполняется каждый раз при выполнении запроса. Другими словами, для запросов LINQ по умолчанию кэшируются не все результаты компиляции.

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

В качестве иллюстрации вы будете делать это для двух Get методов в SchoolRepository классе, один из которых не принимает никаких параметров ( GetInstructorNames метод), а другой требует параметр ( GetDepartmentsByAdministrator метод ). Эти методы в том виде, в который они сейчас скомпилированы, на самом деле не нужно компилировать, так как они не являются запросами LINQ:

public IEnumerable<InstructorName> GetInstructorNames()
{
    return context.InstructorNames.OrderBy("it.FullName").ToList();
}
public IEnumerable<Department> GetDepartmentsByAdministrator(Int32 administrator)
{
    return new ObjectQuery<Department>("SELECT VALUE d FROM Departments as d", context, MergeOption.NoTracking).Include("Person").Where(d => d.Administrator == administrator).ToList();
}

Тем не менее, чтобы вы могли опробовать скомпилированные запросы, вы будете действовать так, как если бы они были написаны как следующие запросы LINQ:

public IEnumerable<InstructorName> GetInstructorNames()
{
    return (from i in context.InstructorNames orderby i.FullName select i).ToList();
}
public IEnumerable<Department> GetDepartmentsByAdministrator(Int32 administrator)
{
    context.Departments.MergeOption = MergeOption.NoTracking;
    return (from d in context.Departments where d.Administrator == administrator select d).ToList();
}

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

Создайте файл класса в папке DAL , назовите его SchoolEntities.cs и замените существующий код следующим кодом:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Objects;

namespace ContosoUniversity.DAL
{
    public partial class SchoolEntities
    {
        private static readonly Func<SchoolEntities, IQueryable<InstructorName>> compiledInstructorNamesQuery =
            CompiledQuery.Compile((SchoolEntities context) => from i in context.InstructorNames orderby i.FullName select i);

        public IEnumerable<InstructorName> CompiledInstructorNamesQuery()
        {
            return compiledInstructorNamesQuery(this).ToList();
        }

        private static readonly Func<SchoolEntities, Int32, IQueryable<Department>> compiledDepartmentsByAdministratorQuery =
            CompiledQuery.Compile((SchoolEntities context, Int32 administrator) => from d in context.Departments.Include("Person") where d.Administrator == administrator select d);

        public IEnumerable<Department> CompiledDepartmentsByAdministratorQuery(Int32 administrator)
        {
            return compiledDepartmentsByAdministratorQuery(this, administrator).ToList();
        }
    }
}

Этот код создает разделяемый класс, который расширяет автоматически создаваемый класс контекста объекта. Разделяемый класс включает два скомпилированных запроса LINQ с помощью Compile метода CompiledQuery класса . Он также создает методы, которые можно использовать для вызова запросов. Сохраните и закройте этот файл.

Затем в Файле SchoolRepository.cs измените существующие GetInstructorNames методы и GetDepartmentsByAdministrator в классе репозитория, чтобы они вызывали скомпилированные запросы:

public IEnumerable<InstructorName> GetInstructorNames()
{
    return context.CompiledInstructorNamesQuery();
}
public IEnumerable<Department> GetDepartmentsByAdministrator(Int32 administrator)
{
    return context.CompiledDepartmentsByAdministratorQuery(administrator);
}

Запустите страницу Departments.aspx , чтобы убедиться, что она работает так же, как и раньше. Метод GetInstructorNames вызывается для заполнения раскрывающегося списка администратора, а GetDepartmentsByAdministrator метод вызывается при нажатии кнопки Обновить , чтобы убедиться, что ни один преподаватель не является администратором нескольких отделов.

Image03

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

Проверка запросов, отправленных в базу данных

При изучении проблем с производительностью иногда полезно знать точные команды SQL, которые Entity Framework отправляет в базу данных. Если вы работаете IQueryable с объектом , один из способов сделать это — использовать ToTraceString метод .

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

public IEnumerable<Department> GetDepartmentsByName(string sortExpression, string nameSearchString)
{
    ...
    var departments = new ObjectQuery<Department>("SELECT VALUE d FROM Departments AS d", context).OrderBy("it." + sortExpression).Include("Person").Include("Courses").Where(d => d.Name.Contains(nameSearchString));
    string commandText = ((ObjectQuery)departments).ToTraceString();
    return departments.ToList();
}

Переменная departments должна быть приведена к типу ObjectQuery только потому, что Where метод в конце предыдущей строки создает IQueryable объект; без Where метода приведение не было бы обязательным.

Задайте точку останова в строке return , а затем запустите страницу Departments.aspx в отладчике. При достижении точки останова проверьте commandText переменную в окне Локальные и используйте визуализатор текста (лупу в столбце Значение ), чтобы отобразить ее значение в окне Визуализатор текста . Вы можете просмотреть всю команду SQL, полученную из этого кода:

Image08

В качестве альтернативы функция IntelliTrace в Visual Studio Ultimate предоставляет способ просмотра команд SQL, созданных Entity Framework, для которых не требуется изменять код или даже устанавливать точку останова.

Примечание

Следующие процедуры можно выполнить только при наличии Visual Studio Ultimate.

Восстановите исходный код в методе GetDepartmentsByName , а затем запустите страницу Departments.aspx в отладчике.

В Visual Studio выберите меню Отладка , Затем IntelliTrace и События IntelliTrace.

Изображение11

В окне IntelliTrace щелкните Разорвать все.

Изображение12

В окне IntelliTrace отображается список последних событий:

Image09

Щелкните строку ADO.NET . Он разворачивается, чтобы отобразить текст команды:

Изображение10

Вы можете скопировать всю текстовую строку команды в буфер обмена из окна Локальные .

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

Например, попробуйте изменить код в методе GetDepartmentsByNameSchoolRepository.cs. В настоящее время в этом методе есть запрос на объект, который содержит Include методы для свойств навигации Person и Courses . Замените инструкцию return кодом, выполняющим явную загрузку, как показано в следующем примере:

public IEnumerable<Department> GetDepartmentsByName(string sortExpression, string nameSearchString)
{
    ...
    var departments = new ObjectQuery<Department>("SELECT VALUE d FROM Departments AS d", context).OrderBy("it." + sortExpression).Where(d => d.Name.Contains(nameSearchString)).ToList();
    foreach (Department d in departments)
    {
        d.Courses.Load();
        d.PersonReference.Load();
    }
    return departments;
}

Запустите страницу Departments.aspx в отладчике и проверка окно IntelliTrace еще раз, как и раньше. Теперь, когда ранее существовал один запрос, вы увидите длинную последовательность из них.

Изображение13

Щелкните первую строку ADO.NET , чтобы узнать, что произошло со сложным запросом, который вы просматривали ранее.

Изображение14

Запрос от отделов стал простым Select запросом без Join предложения, но за ним следуют отдельные запросы, которые извлекают связанные курсы и администратора, используя набор из двух запросов для каждого отдела, возвращаемых исходным запросом.

Примечание

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

Предварительное создание представлений

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

Примечание

Если приложение не имеет очень большой модели данных или имеет модель больших данных, но вы не беспокоитесь о проблеме с производительностью, которая влияет только на самый первый запрос страницы после перезапуска IIS, этот раздел можно пропустить. Создание представления происходит не каждый раз при создании экземпляра ObjectContext объекта, так как представления кэшируются в домене приложения. Таким образом, если вы часто не перезапуските приложение в СЛУЖБАх IIS, очень немногие запросы страниц будут полезны от предварительно созданных представлений.

Представления можно предварительно создать с помощью программы командной строкиEdmGen.exe или с помощью шаблона T4. В этом руководстве вы будете использовать шаблон T4.

В папке DAL добавьте файл с помощью шаблона Текстовый шаблон (он находится в узле Общие в списке Установленные шаблоны ) и присвойте ему имя SchoolModel.Views.tt. Замените существующий код в файле следующим кодом:

<#
/***************************************************************************

Copyright (c) Microsoft Corporation. All rights reserved.

THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.

***************************************************************************/
#>

<#
    //
    // TITLE: T4 template to generate views for an EDMX file in a C# project
    //
    // DESCRIPTION:
    // This is a T4 template to generate views in C# for an EDMX file in C# projects.
    // The generated views are automatically compiled into the project's output assembly.
    //
    // This template follows a simple file naming convention to determine the EDMX file to process:
    // - It assumes that [edmx-file-name].Views.tt will process and generate views for [edmx-file-name].EDMX
    // - The views are generated in the code behind file [edmx-file-name].Views.cs
    //
    // USAGE:
    // Do the following to generate views for an EDMX file (e.g. Model1.edmx) in a C# project
    // 1. In Solution Explorer, right-click the project node and choose "Add...Existing...Item" from the context menu
    // 2. Browse to and choose this .tt file to include it in the project 
    // 3. Ensure this .tt file is in the same directory as the EDMX file to process 
    // 4. In Solution Explorer, rename this .tt file to the form [edmx-file-name].Views.tt (e.g. Model1.Views.tt)
    // 5. In Solution Explorer, right-click Model1.Views.tt and choose "Run Custom Tool" to generate the views
    // 6. The views are generated in the code behind file Model1.Views.cs
    //
    // TIPS:
    // If you have multiple EDMX files in your project then make as many copies of this .tt file and rename appropriately
    // to pair each with each EDMX file.
    //
    // To generate views for all EDMX files in the solution, click the "Transform All Templates" button in the Solution Explorer toolbar
    // (its the rightmost button in the toolbar) 
    //
#>
<#
    //
    // T4 template code follows
    //
#>
<#@ template language="C#" hostspecific="true"#>
<#@ include file="EF.Utility.CS.ttinclude"#>
<#@ output extension=".cs" #>
<# 
    // Find EDMX file to process: Model1.Views.tt generates views for Model1.EDMX
    string edmxFileName = Path.GetFileNameWithoutExtension(this.Host.TemplateFile).ToLowerInvariant().Replace(".views", "") + ".edmx";
    string edmxFilePath = Path.Combine(Path.GetDirectoryName(this.Host.TemplateFile), edmxFileName);
    if (File.Exists(edmxFilePath))
    {
        // Call helper class to generate pre-compiled views and write to output
        this.WriteLine(GenerateViews(edmxFilePath));
    }
    else
    {
        this.Error(String.Format("No views were generated. Cannot find file {0}. Ensure the project has an EDMX file and the file name of the .tt file is of the form [edmx-file-name].Views.tt", edmxFilePath));
    }
    
    // All done!
#>

<#+
    private String GenerateViews(string edmxFilePath)
    {
        MetadataLoader loader = new MetadataLoader(this);
        MetadataWorkspace workspace;
        if(!loader.TryLoadAllMetadata(edmxFilePath, out workspace))
        {
            this.Error("Error in the metadata");
            return String.Empty;
        }
            
        String generatedViews = String.Empty;
        try
        {
            using (StreamWriter writer = new StreamWriter(new MemoryStream()))
            {
                StorageMappingItemCollection mappingItems = (StorageMappingItemCollection)workspace.GetItemCollection(DataSpace.CSSpace);

                // Initialize the view generator to generate views in C#
                EntityViewGenerator viewGenerator = new EntityViewGenerator();
                viewGenerator.LanguageOption = LanguageOption.GenerateCSharpCode;
                IList<EdmSchemaError> errors = viewGenerator.GenerateViews(mappingItems, writer);

                foreach (EdmSchemaError e in errors)
                {
                    // log error
                    this.Error(e.Message);
                }

                MemoryStream memStream = writer.BaseStream as MemoryStream;
                generatedViews = Encoding.UTF8.GetString(memStream.ToArray());
            }
        }
        catch (Exception ex)
        {
            // log error
            this.Error(ex.ToString());
        }

        return generatedViews;
    }
#>

Этот код создает представления для EDMX-файла , который находится в той же папке, что и шаблон, и имя которого совпадает с именем файла шаблона. Например, если файл шаблона называется SchoolModel.Views.tt, он будет искать файл модели данных с именем SchoolModel.edmx.

Сохраните файл, щелкните его правой кнопкой мыши в Обозреватель решений и выберите Запустить пользовательское средство.

Image02

Visual Studio создает файл кода, который создает представления с именем SchoolModel.Views.cs на основе шаблона. (Возможно, вы заметили, что файл кода создается еще до нажатия кнопки Запустить пользовательское средство, сразу после сохранения файла шаблона.)

Изображение01

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

Дополнительные сведения о предварительно созданных представлениях см. в следующих ресурсах:

На этом завершается введение в повышение производительности в веб-приложении ASP.NET, использующим Entity Framework. Дополнительные сведения см. в следующих ресурсах:

В следующем руководстве рассматриваются некоторые важные улучшения Entity Framework, которые являются новыми в версии 4.