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

Том Дайкстра

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

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

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

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

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

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

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

  • Предгенерируйте представления.

Замечание

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

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

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

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

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

    Image05

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

Image07

  • Явная загрузка. Это похоже на отложенную загрузку, за исключением того, что вы явно извлекаете связанные данные в коде; Это не происходит автоматически при доступе к свойству навигации. Вы загружаете связанные данные вручную с помощью 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 разработчик приложений Web Forms: свойство ViewStateMode. Это новое свойство можно задать на уровне страницы или элемента управления, и оно позволяет отключить состояние просмотра по умолчанию для страницы и включить его только для элементов управления, которым он нужен.

Для приложений, где производительность является критически важной, рекомендуется всегда отключить состояние просмотра на уровне страницы и включить его только для элементов управления, требующих его. Размер состояния представления на страницах Университета Contoso не будет существенно уменьшен этим методом, но чтобы узнать, как это работает, вы сделаете это для страницы Instructors.aspx . Эта страница содержит множество элементов управления, включая Label элемент управления с отключенным состоянием представления. Ни один из элементов управления на этой странице фактически не должен включать состояние просмотра. (Свойство DataKeyNames элемента управления GridView указывает состояние, которое должно поддерживаться между постбэками, но эти значения хранятся в состоянии управления, что не затрагивается свойством 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

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

В 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

При первом выполнении запроса SQL в Entity Framework в течение срока существования данного экземпляра 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, чтобы узнать, как это сделать, а не потому, что это позволит значительно повысить производительность. Предварительно скомпилированные запросы 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.

Image11

В окне IntelliTrace нажмите кнопку "Разорвать все".

Image12

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

Image09

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

Image10

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

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

Например, попробуйте изменить код в методе GetDepartmentsByName в SchoolRepository.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 , как и раньше. Теперь, где раньше был только один запрос, вы видите длинную последовательность их.

Image13

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

Image14

Запрос из отделов стал простым 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 на основе шаблона. (Возможно, вы заметили, что файл кода создается ещё до того, как вы выберете Запустить Пользовательский Инструмент, как только вы сохраните файл шаблона.)

Image01

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

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

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

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