Maksymalizowanie wydajności za pomocą programu Entity Framework 4.0 w aplikacji internetowej ASP.NET 4

Autor : Tom Dykstra

Ta seria samouczków opiera się na aplikacji internetowej Contoso University utworzonej przez Wprowadzenie z serii samouczków Entity Framework 4.0. Jeśli nie ukończysz wcześniejszych samouczków, jako punkt wyjścia dla tego samouczka możesz pobrać utworzoną aplikację . Możesz również pobrać aplikację utworzoną przez kompletną serię samouczków. Jeśli masz pytania dotyczące samouczków, możesz opublikować je na forum ASP.NET Entity Framework.

W poprzednim samouczku pokazano, jak obsługiwać konflikty współbieżności. W tym samouczku przedstawiono opcje poprawy wydajności ASP.NET aplikacji internetowej korzystającej z programu Entity Framework. Poznasz kilka metod maksymalizacji wydajności lub diagnozowania problemów z wydajnością.

Informacje przedstawione w poniższych sekcjach mogą być przydatne w wielu różnych scenariuszach:

  • Wydajne ładowanie powiązanych danych.
  • Zarządzanie stanem widoku.

Informacje przedstawione w poniższych sekcjach mogą być przydatne, jeśli masz pojedyncze zapytania, które przedstawiają problemy z wydajnością:

  • NoTracking Użyj opcji scalania.
  • Wstępne kompilowanie zapytań LINQ.
  • Sprawdź polecenia zapytania wysyłane do bazy danych.

Informacje przedstawione w poniższej sekcji są potencjalnie przydatne w przypadku aplikacji, które mają bardzo duże modele danych:

  • Wstępnie generuj widoki.

Uwaga

Wydajność aplikacji internetowej ma wpływ na wiele czynników, takich jak rozmiar żądań i danych odpowiedzi, szybkość zapytań bazy danych, liczba żądań, które serwer może umieścić w kolejce i jak szybko może je obsługiwać, a nawet wydajność dowolnych bibliotek skryptów klienta, których można używać. Jeśli wydajność jest krytyczna w aplikacji lub jeśli testowanie lub środowisko pokazuje, że wydajność aplikacji nie jest zadowalająca, należy postępować zgodnie z normalnym protokołem dostrajania wydajności. Miara w celu określenia, gdzie występują wąskie gardła wydajności, a następnie zająć się obszarami, które będą miały największy wpływ na ogólną wydajność aplikacji.

W tym temacie skupiono się głównie na sposobach, w których można potencjalnie poprawić wydajność programu Entity Framework w ASP.NET. Sugestie są tutaj przydatne, jeśli określisz, że dostęp do danych jest jednym z wąskich gardeł wydajności w aplikacji. Poza tym, jak wspomniano, metody opisane tutaj nie powinny być traktowane jako "najlepsze rozwiązania" w ogóle — wiele z nich jest odpowiednich tylko w wyjątkowych sytuacjach lub w celu rozwiązania bardzo specyficznych rodzajów wąskich gardeł wydajności.

Aby rozpocząć samouczek, uruchom program Visual Studio i otwórz aplikację internetową Contoso University, z którą pracujesz w poprzednim samouczku.

Istnieje kilka sposobów ładowania powiązanych danych przez program Entity Framework do właściwości nawigacji jednostki:

  • Ładowanie z opóźnieniem. Gdy jednostka jest najpierw odczytywana, powiązane dane nie są pobierane. Jednak po raz pierwszy próbujesz uzyskać dostęp do właściwości nawigacji, dane wymagane dla tej właściwości nawigacji są pobierane automatycznie. Powoduje to wysłanie wielu zapytań do bazy danych — jeden dla samej jednostki i jeden za każdym razem, gdy należy pobrać powiązane dane dla jednostki.

    Obraz05

Chętny do ładowania. Gdy jednostka jest odczytywana, powiązane dane są pobierane wraz z nim. Zazwyczaj powoduje to utworzenie pojedynczego zapytania sprzężenia, które pobiera wszystkie potrzebne dane. Możesz określić chętne ładowanie przy użyciu Include metody , jak pokazano już w tych samouczkach.

Obraz07

  • Jawne ładowanie. Jest to podobne do ładowania leniwego, z wyjątkiem tego, że jawnie pobierasz powiązane dane w kodzie; nie występuje automatycznie po uzyskiwaniu dostępu do właściwości nawigacji. Dane powiązane są ładowane ręcznie przy użyciu Load metody właściwości nawigacji dla kolekcji lub metody Load właściwości odwołania dla właściwości, które przechowują pojedynczy obiekt. (Na przykład wywołasz metodę PersonReference.Load , aby załadować Person właściwość Department nawigacji jednostki).

    Obraz06

Ponieważ nie pobierają natychmiast wartości właściwości, ładowanie leniwe i jawne ładowanie są również nazywane ładowaniem odroczonym.

Ładowanie z opóźnieniem jest domyślnym zachowaniem kontekstu obiektu, który został wygenerowany przez projektanta. Jeśli otworzysz plik SchoolModel.Projektant. Plik cs, który definiuje klasę kontekstu obiektu, znajdziesz trzy metody konstruktora, a każda z nich zawiera następującą instrukcję:

this.ContextOptions.LazyLoadingEnabled = true;

Ogólnie rzecz biorąc, jeśli wiesz, że potrzebujesz powiązanych danych dla każdej pobranej jednostki, chętne ładowanie oferuje najlepszą wydajność, ponieważ pojedyncze zapytanie wysyłane do bazy danych jest zazwyczaj bardziej wydajne niż oddzielne zapytania dla każdej pobranej jednostki. Z drugiej strony, jeśli potrzebujesz dostępu do właściwości nawigacji jednostki tylko rzadko lub tylko w przypadku małego zestawu jednostek, ładowanie leniwe lub jawne ładowanie może być bardziej wydajne, ponieważ chętne ładowanie pobiera więcej danych niż potrzebujesz.

W aplikacji internetowej ładowanie leniwe może być w stosunkowo małej wartości, ponieważ akcje użytkownika, które wpływają na potrzebę powiązanych danych odbywa się w przeglądarce, co nie ma połączenia z kontekstem obiektu, który renderował stronę. Z drugiej strony w przypadku powiązania kontrolki zwykle wiadomo, jakich danych potrzebujesz, i dlatego zazwyczaj najlepiej jest wybrać chętne ładowanie lub odroczone ładowanie na podstawie tego, co jest odpowiednie w każdym scenariuszu.

Ponadto kontrolka ruchu przychodzącego może używać obiektu jednostki po usunięciu kontekstu obiektu. W takim przypadku próba leniwego załadowania właściwości nawigacji zakończy się niepowodzeniem. Wyświetlany komunikat o błędzie jest jasny: "The ObjectContext instance has been disposed and can no longer be used for operations that require a connection."

Kontrolka EntityDataSource domyślnie wyłącza ładowanie leniwe. ObjectDataSource W przypadku kontrolki używanej w bieżącym samouczku (lub w przypadku uzyskiwania dostępu do kontekstu obiektu z kodu strony) możesz domyślnie wyłączyć ładowanie leniwe. Można go wyłączyć po utworzeniu wystąpienia kontekstu obiektu. Można na przykład dodać następujący wiersz do metody konstruktora SchoolRepository klasy:

context.ContextOptions.LazyLoadingEnabled = false;

W przypadku aplikacji Contoso University kontekst obiektu zostanie automatycznie wyłączony z opóźnieniem ładowania, aby ta właściwość nie musiała być ustawiana za każdym razem, gdy kontekst zostanie utworzone.

Otwórz model danych SchoolModel.edmx , kliknij powierzchnię projektową, a następnie w okienku właściwości Ustaw właściwość Włączone ładowanie leniwe na False. Zapisz i zamknij model danych.

Obraz04

Zarządzanie stanem widoku

Aby zapewnić funkcjonalność aktualizacji, strona internetowa ASP.NET musi przechowywać oryginalne wartości właściwości jednostki po renderowaniu strony. Podczas przetwarzania zwrotnego kontrolka może ponownie utworzyć oryginalny stan jednostki i wywołać metodę jednostki Attach przed zastosowaniem zmian i wywołaniem SaveChanges metody . Domyślnie ASP.NET Web Forms kontrolki danych używają stanu widoku do przechowywania oryginalnych wartości. Jednak stan widoku może mieć wpływ na wydajność, ponieważ jest on przechowywany w ukrytych polach, które mogą znacznie zwiększyć rozmiar strony wysyłanej do i z przeglądarki.

Techniki zarządzania stanem widoku lub alternatywy, takie jak stan sesji, nie są unikatowe dla programu Entity Framework, więc w tym samouczku nie opisano szczegółowo tego tematu. Aby uzyskać więcej informacji, zobacz linki na końcu samouczka.

Jednak wersja 4 ASP.NET zapewnia nowy sposób pracy z stanem wyświetlania, który powinien być świadomy każdego ASP.NET dewelopera aplikacji Web Forms: ViewStateMode właściwości. Ta nowa właściwość może być ustawiana na poziomie strony lub kontrolki i umożliwia domyślnie wyłączenie stanu widoku dla strony i włączenie jej tylko dla kontrolek, które ich potrzebują.

W przypadku aplikacji, w których wydajność jest krytyczna, dobrym rozwiązaniem jest zawsze wyłączenie stanu widoku na poziomie strony i włączenie go tylko dla kontrolek, które tego wymagają. Rozmiar stanu widoku na stronach Uniwersytetu Contoso nie zostałby znacznie zmniejszony przez tę metodę, ale aby zobaczyć, jak to działa, zrobisz to dla strony Instruktorzy.aspx . Ta strona zawiera wiele kontrolek, w tym kontrolkę Label , która ma wyłączony stan widoku. Żadna z kontrolek na tej stronie nie musi mieć włączonego stanu wyświetlania. (Właściwość DataKeyNames kontrolki GridView określa stan, który musi być utrzymywany między ponarodkami, ale te wartości są przechowywane w stanie kontroli, który nie ma wpływu na ViewStateMode właściwość).

Dyrektywa Page i Label znaczniki kontroli są obecnie podobne do następującego przykładu:

<%@ 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> 
    ...

Wprowadź następujące zmiany:

  • Dodaj ViewStateMode="Disabled" do Page dyrektywy.
  • Usuń ViewStateMode="Disabled" z kontrolki Label .

Znaczniki są teraz podobne do następującego przykładu:

<%@ 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> 
    ...

Stan widoku jest teraz wyłączony dla wszystkich kontrolek. Jeśli później dodasz kontrolkę, która musi używać stanu widoku, wystarczy uwzględnić ViewStateMode="Enabled" atrybut dla tej kontrolki.

Korzystanie z opcji Scalanie noTracking

Gdy kontekst obiektu pobiera wiersze bazy danych i tworzy obiekty jednostki, które je reprezentują, domyślnie śledzi również te obiekty jednostki przy użyciu menedżera stanu obiektu. Te dane śledzenia działają jako pamięć podręczna i są używane podczas aktualizowania jednostki. Ponieważ aplikacja internetowa zwykle ma krótkotrwałe wystąpienia kontekstu obiektu, zapytania często zwracają dane, które nie muszą być śledzone, ponieważ kontekst obiektu, który je odczytuje, zostanie usunięty przed ponownym lub zaktualizowanymi jednostkami, które odczytuje.

W programie Entity Framework można określić, czy kontekst obiektu śledzi obiekty jednostki, ustawiając opcję scalania. Można ustawić opcję scalania dla poszczególnych zapytań lub zestawów jednostek. Jeśli ustawisz go dla zestawu jednostek, oznacza to, że ustawiasz domyślną opcję scalania dla wszystkich zapytań utworzonych dla tego zestawu jednostek.

W przypadku aplikacji Contoso University śledzenie nie jest wymagane w przypadku żadnego z zestawów jednostek, do których uzyskujesz dostęp z repozytorium, dzięki czemu można ustawić opcję NoTracking scalania dla tych zestawów jednostek podczas tworzenia wystąpienia kontekstu obiektu w klasie repozytorium. (Pamiętaj, że w tym samouczku ustawienie opcji scalania nie będzie miało zauważalnego wpływu na wydajność aplikacji. Ta NoTracking opcja może spowodować zauważalną poprawę wydajności tylko w niektórych scenariuszach o dużej ilości danych).

W folderze DAL otwórz plik SchoolRepository.cs i dodaj metodę konstruktora, która ustawia opcję scalania dla zestawów jednostek, do których uzyskuje dostęp repozytorium:

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

Wstępne kompilowanie zapytań LINQ

Po raz pierwszy, gdy platforma Entity Framework wykonuje zapytanie SQL jednostki w ciągu życia danego ObjectContext wystąpienia, skompilowanie zapytania zajmuje trochę czasu. Wynik kompilacji jest buforowany, co oznacza, że kolejne wykonania zapytania są znacznie szybsze. Zapytania LINQ są zgodne z podobnym wzorcem, z wyjątkiem tego, że niektóre zadania wymagane do skompilowania zapytania są wykonywane za każdym razem, gdy zapytanie jest wykonywane. Innymi słowy, w przypadku zapytań LINQ domyślnie nie wszystkie wyniki kompilacji są buforowane.

Jeśli masz zapytanie LINQ, które ma być uruchamiane wielokrotnie w okresie życia kontekstu obiektu, możesz napisać kod, który powoduje buforowanie wszystkich wyników kompilacji przy pierwszym uruchomieniu zapytania LINQ.

Na ilustracji wykonasz tę czynność dla dwóch Get metod w SchoolRepository klasie, z których jedna nie bierze żadnych parametrów ( GetInstructorNames metody) i tych, które wymagają parametru ( GetDepartmentsByAdministrator metoda). Te metody, które stoją teraz, nie muszą być kompilowane, ponieważ nie są to zapytania 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();
}

Jednak w celu wypróbowania skompilowanych zapytań będziesz kontynuować tak, jakby zostały one napisane jako następujące zapytania 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();
}

Możesz zmienić kod w tych metodach na pokazane powyżej i uruchomić aplikację, aby sprawdzić, czy działa przed kontynuowaniem. Jednak poniższe instrukcje przeskoczą bezpośrednio do tworzenia wstępnie skompilowanych wersji.

Utwórz plik klasy w folderze DAL , nadaj mu nazwę SchoolEntities.cs i zastąp istniejący kod następującym kodem:

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();
        }
    }
}

Ten kod tworzy klasę częściową, która rozszerza automatycznie wygenerowaną klasę kontekstu obiektu. Klasa częściowa zawiera dwa skompilowane zapytania LINQ przy użyciu Compile metody CompiledQuery klasy . Tworzy również metody, których można użyć do wywoływania zapytań. Zapisz i zamknij ten plik.

Następnie w pliku SchoolRepository.cs zmień istniejące GetInstructorNames metody i GetDepartmentsByAdministrator w klasie repozytorium, aby wywoływać skompilowane zapytania:

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

Uruchom stronę Departments.aspx , aby sprawdzić, czy działa ona tak jak poprzednio. Metoda GetInstructorNames jest wywoływana w celu wypełnienia listy rozwijanej administratora, a GetDepartmentsByAdministrator metoda jest wywoływana po kliknięciu przycisku Aktualizuj , aby sprawdzić, czy żaden instruktor nie jest administratorem więcej niż jednego działu.

Obraz03

Zapytania zostały wstępnie skompilowane w aplikacji Contoso University tylko po to, aby zobaczyć, jak to zrobić, a nie dlatego, że zapewniłoby to wysoką wydajność. Wstępne kompilowanie zapytań LINQ zwiększa poziom złożoności kodu, dlatego upewnij się, że wykonujesz je tylko w przypadku zapytań, które rzeczywiście reprezentują wąskie gardła wydajności w aplikacji.

Badanie zapytań wysyłanych do bazy danych

Podczas badania problemów z wydajnością czasami warto znać dokładne polecenia SQL wysyłane przez program Entity Framework do bazy danych. Jeśli pracujesz z obiektem IQueryable , jednym ze sposobów wykonania tej czynności jest użycie ToTraceString metody .

W pliku SchoolRepository.cs zmień kod w metodzie GetDepartmentsByName , aby był zgodny z poniższym przykładem:

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();
}

Zmienna departments musi być rzutowania na ObjectQuery typ tylko dlatego, że Where metoda na końcu poprzedniego wiersza tworzy IQueryable obiekt; bez Where metody rzutowanie nie byłoby konieczne.

Ustaw punkt przerwania w return wierszu, a następnie uruchom stronę Departments.aspx w debugerze. Po osiągnięciu punktu przerwania sprawdź zmienną commandText w oknie Ustawienia lokalne i użyj wizualizatora tekstu (lupy w kolumnie Wartość ), aby wyświetlić jej wartość w oknie Wizualizator tekstu . Zobaczysz całe polecenie SQL, które wynika z tego kodu:

Obraz08

Alternatywnie funkcja IntelliTrace w systemie Visual Studio Ultimate umożliwia wyświetlanie poleceń SQL generowanych przez program Entity Framework, które nie wymagają zmiany kodu, a nawet ustawiania punktu przerwania.

Uwaga

Poniższe procedury można wykonać tylko wtedy, gdy masz Visual Studio Ultimate.

Przywróć oryginalny kod w metodzie GetDepartmentsByName , a następnie uruchom stronę Departments.aspx w debugerze.

W programie Visual Studio wybierz menu Debuguj , a następnie pozycję IntelliTrace, a następnie zdarzenia IntelliTrace.

Obraz11

W oknie IntelliTrace kliknij pozycję Przerwij wszystko.

Obraz12

W oknie IntelliTrace zostanie wyświetlona lista ostatnich zdarzeń:

Obraz09

Kliknij wiersz ADO.NET . Rozwija się, aby wyświetlić tekst polecenia:

Obraz10

Cały ciąg tekstowy polecenia można skopiować do schowka w oknie Ustawienia lokalne .

Załóżmy, że pracujesz z bazą danych z większą ilością tabel, relacji i kolumn niż prosta School baza danych. Może się okazać, że zapytanie, które zbiera wszystkie informacje potrzebne w jednej Select instrukcji zawierającej wiele Join klauzul, staje się zbyt złożone, aby wydajnie pracować. W takim przypadku możesz przełączyć się z chętnego ładowania do jawnego ładowania, aby uprościć zapytanie.

Na przykład spróbuj zmienić kod w metodzie GetDepartmentsByName w pliku SchoolRepository.cs. Obecnie w tej metodzie masz zapytanie obiektu, które ma Include metody właściwości Person nawigacji i .Courses Zastąp instrukcję return kodem, który wykonuje jawne ładowanie, jak pokazano w poniższym przykładzie:

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;
}

Uruchom stronę Departments.aspx w debugerze i ponownie sprawdź okno IntelliTrace , tak jak wcześniej. Teraz, gdzie wcześniej istniało pojedyncze zapytanie, zostanie wyświetlona długa sekwencja.

Obraz13

Kliknij pierwszy wiersz ADO.NET , aby zobaczyć, co się stało z wyświetlonym wcześniej złożonym zapytaniem.

Obraz14

Zapytanie z działów stało się prostym Select zapytaniem bez Join klauzuli, ale następuje oddzielne zapytania, które pobierają powiązane kursy i administrator, używając zestawu dwóch zapytań dla każdego działu zwróconego przez oryginalne zapytanie.

Uwaga

Jeśli pozostawisz włączone ładowanie z opóźnieniem, wzorzec widoczny w tym miejscu, z tym samym zapytaniem powtarzane wiele razy, może wynikać z opóźnionego ładowania. Wzorzec, którego zwykle chcesz uniknąć, to leniwe ładowanie powiązanych danych dla każdego wiersza tabeli podstawowej. Jeśli nie sprawdzono, że pojedyncze zapytanie sprzężenia jest zbyt złożone, aby było wydajne, zwykle można poprawić wydajność w takich przypadkach przez zmianę zapytania podstawowego w celu korzystania z chętnego ładowania.

Wstępne generowanie widoków

Po pierwszym utworzeniu ObjectContext obiektu w nowej domenie aplikacji program Entity Framework generuje zestaw klas używanych do uzyskiwania dostępu do bazy danych. Te klasy są nazywane widokami, a jeśli masz bardzo duży model danych, generowanie tych widoków może opóźnić odpowiedź witryny internetowej na pierwsze żądanie strony po zainicjowaniu nowej domeny aplikacji. To opóźnienie pierwszego żądania można zmniejszyć, tworząc widoki w czasie kompilacji, a nie w czasie wykonywania.

Uwaga

Jeśli aplikacja nie ma bardzo dużego modelu danych lub jeśli ma duży model danych, ale nie interesuje Cię problem z wydajnością, który dotyczy tylko pierwszego żądania strony po przetworzeniu usług IIS, możesz pominąć tę sekcję. Tworzenie widoku nie odbywa się za każdym razem, gdy tworzy się wystąpienie ObjectContext obiektu, ponieważ widoki są buforowane w domenie aplikacji. W związku z tym, chyba że często przetwarzasz aplikację w usługach IIS, bardzo niewiele żądań stron może korzystać ze wstępnie wygenerowanych widoków.

Widoki można wstępnie wygenerować przy użyciu narzędzia wiersza polecenia EdmGen.exe lub szablonu zestawu narzędzi do przekształcania szablonów tekstu (T4). W tym samouczku użyjesz szablonu T4.

W folderze DAL dodaj plik przy użyciu szablonu szablonu tekstowego (znajduje się on w węźle Ogólne na liście Zainstalowane szablony ) i nadaj mu nazwę SchoolModel.Views.tt. Zastąp istniejący kod w pliku następującym kodem:

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

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;
    }
#>

Ten kod generuje widoki dla pliku edmx znajdującego się w tym samym folderze co szablon i ma taką samą nazwę jak plik szablonu. Jeśli na przykład plik szablonu ma nazwę SchoolModel.Views.tt, będzie on szukać pliku modelu danych o nazwie SchoolModel.edmx.

Zapisz plik, a następnie kliknij prawym przyciskiem myszy plik w Eksplorator rozwiązań i wybierz polecenie Uruchom narzędzie niestandardowe.

Obraz02

Program Visual Studio generuje plik kodu, który tworzy widoki o nazwie SchoolModel.Views.cs na podstawie szablonu. (Być może zauważysz, że plik kodu jest generowany nawet przed wybraniem polecenia Uruchom narzędzie niestandardowe, gdy tylko zapiszesz plik szablonu).

Obraz01

Teraz możesz uruchomić aplikację i sprawdzić, czy działa ona tak, jak wcześniej.

Aby uzyskać więcej informacji na temat wstępnie wygenerowanych widoków, zobacz następujące zasoby:

Spowoduje to ukończenie wprowadzenia do poprawy wydajności w ASP.NET aplikacji internetowej korzystającej z programu Entity Framework. Więcej informacji można znaleźć w następujących zasobach:

W następnym samouczku omówiono niektóre ważne ulepszenia programu Entity Framework, które są nowe w wersji 4.