Używanie programu Entity Framework 4.0 i kontrolki ObjectDataSource, część 1: Wprowadzenie

Autor : Tom Dykstra

Ta seria samouczków jest oparta na aplikacji internetowej Contoso University utworzonej przez Wprowadzenie z serii samouczków platformy Entity Framework 4.0. Jeśli nie wykonasz wcześniejszych samouczków, jako punkt wyjścia dla tego samouczka możesz pobrać utworzoną aplikację . Możesz również pobrać aplikację utworzoną w ramach pełnej serii samouczków.

Przykładowa aplikacja internetowa Contoso University pokazuje, jak tworzyć aplikacje ASP.NET Web Forms przy użyciu platform Entity Framework 4.0 i Visual Studio 2010. Przykładowa aplikacja jest witryną internetową fikcyjnego uniwersytetu Contoso. Obejmuje ona funkcje, takie jak wstęp do studentów, tworzenie kursu i zadania instruktora.

W samouczku przedstawiono przykłady w języku C#. Przykład do pobrania zawiera kod w językach C# i Visual Basic.

Najpierw baza danych

Istnieją trzy sposoby pracy z danymi w programie Entity Framework: Database First, Model First i Code First. Ten samouczek jest przeznaczony dla usługi Database First. Aby uzyskać informacje o różnicach między tymi przepływami pracy i wskazówkami dotyczącymi wybierania najlepszego scenariusza, zobacz Entity Framework Development Workflows (Przepływy pracy programowania w programie Entity Framework).

Formularze sieci Web

Podobnie jak w przypadku serii Wprowadzenie, ta seria samouczków korzysta z modelu ASP.NET Web Forms i zakłada, że wiesz, jak pracować z ASP.NET Web Forms w programie Visual Studio. Jeśli nie, zobacz Wprowadzenie z ASP.NET 4.5 Web Forms. Jeśli wolisz pracować z platformą ASP.NET MVC, zobacz Wprowadzenie z programem Entity Framework przy użyciu ASP.NET MVC.

Wersje oprogramowania

Pokazano w samouczku Działa również z
Windows 7 Windows 8
Visual Studio 2010 Visual Studio 2010 Express for Web. Samouczek nie został przetestowany z nowszymi wersjami programu Visual Studio. Istnieje wiele różnic w opcjach menu, oknach dialogowych i szablonach.
.NET 4 Platforma .NET 4.5 jest zgodna z poprzednimi wersjami platformy .NET 4, ale samouczek nie został przetestowany z platformą .NET 4.5.
Entity Framework 4 Samouczek nie został przetestowany z nowszymi wersjami programu Entity Framework. Począwszy od programu Entity Framework 5, program EF domyślnie używa programu EF wprowadzonego DbContext API w programie EF 4.1. Kontrolka EntityDataSource została zaprojektowana do korzystania z interfejsu ObjectContext API. Aby uzyskać informacje na temat używania kontrolki EntityDataSource z interfejsem DbContext API, zobacz ten wpis w blogu.

Pytania

Jeśli masz pytania, które nie są bezpośrednio związane z tym samouczkiem, możesz opublikować je na forum ASP.NET Entity Framework, forum Entity Framework i LINQ to Entities lub StackOverflow.com.

Kontrolka EntityDataSource umożliwia bardzo szybkie tworzenie aplikacji, ale zwykle wymaga przechowywania znacznej ilości logiki biznesowej i logiki dostępu do danych na stronach aspx . Jeśli oczekujesz, że aplikacja wzrośnie w złożoności i będzie wymagała ciągłej konserwacji, możesz zainwestować więcej czasu programowania z góry, aby utworzyć n-warstwową lub warstwową strukturę aplikacji, która jest bardziej możliwa do utrzymania. Aby zaimplementować tę architekturę, należy oddzielić warstwę prezentacji od warstwy logiki biznesowej (BLL) i warstwy dostępu do danych (DAL). Jednym ze sposobów zaimplementowania tej struktury jest użycie kontrolki ObjectDataSource zamiast kontrolki EntityDataSource . Gdy używasz kontrolki ObjectDataSource , zaimplementujesz własny kod dostępu do danych, a następnie wywołaj go na stronach aspx przy użyciu kontrolki, która ma wiele tych samych funkcji co inne kontrolki źródła danych. Pozwala to połączyć zalety podejścia n-warstwowego z korzyściami korzystania z Web Forms kontroli dostępu do danych.

Kontrolka ObjectDataSource zapewnia większą elastyczność na inne sposoby. Ponieważ piszesz własny kod dostępu do danych, łatwiej jest wykonywać więcej czynności niż tylko odczytywanie, wstawianie, aktualizowanie lub usuwanie określonego typu jednostki, które są zadaniami EntityDataSource przeznaczonymi do wykonania przez kontrolkę. Można na przykład wykonywać rejestrowanie za każdym razem, gdy jednostka jest aktualizowana, archiwizować dane przy każdym usunięciu jednostki lub automatycznie sprawdzać i aktualizować powiązane dane w razie potrzeby podczas wstawiania wiersza z wartością klucza obcego.

Klasy logiki biznesowej i repozytorium

Kontrolka ObjectDataSource działa przez wywołanie tworzonej klasy. Klasa zawiera metody pobierające i aktualizujące dane, a także podaje nazwy tych metod do kontrolki ObjectDataSource w adiustacji. Podczas renderowania lub przetwarzania ObjectDataSource zwrotnego metody są wywoływane określone metody.

Oprócz podstawowych operacji CRUD klasa utworzona do użycia z kontrolką ObjectDataSource może wymagać wykonania logiki biznesowej podczas ObjectDataSource odczytywania lub aktualizowania danych. Na przykład podczas aktualizowania działu może być konieczne sprawdzenie, czy żaden inny dział nie ma tego samego administratora, ponieważ jedna osoba nie może być administratorem więcej niż jednego działu.

W niektórych ObjectDataSource dokumentach, takich jak omówienie klasy ObjectDataSource, kontrolka wywołuje klasę nazywaną obiektem biznesowym , który zawiera zarówno logikę biznesową, jak i logikę dostępu do danych. W tym samouczku utworzysz oddzielne klasy dla logiki biznesowej i logiki dostępu do danych. Klasa, która hermetyzuje logikę dostępu do danych, jest nazywana repozytorium. Klasa logiki biznesowej zawiera zarówno metody logiki biznesowej, jak i metody dostępu do danych, ale metody dostępu do danych wywołają repozytorium w celu wykonywania zadań dostępu do danych.

Utworzysz również warstwę abstrakcji między BLL i DAL, która ułatwia zautomatyzowane testowanie jednostkowe BLL. Ta warstwa abstrakcji jest implementowana przez utworzenie interfejsu i użycie interfejsu podczas tworzenia wystąpienia repozytorium w klasie logiki biznesowej. Dzięki temu można udostępnić klasę logiki biznesowej przy użyciu odwołania do dowolnego obiektu implementujące interfejs repozytorium. W przypadku normalnego działania należy podać obiekt repozytorium, który współpracuje z programem Entity Framework. Na potrzeby testowania należy podać obiekt repozytorium, który współdziała z danymi przechowywanymi w sposób, który można łatwo manipulować, na przykład zmiennych klas zdefiniowanych jako kolekcje.

Poniższa ilustracja przedstawia różnicę między klasą logiki biznesowej, która zawiera logikę dostępu do danych bez repozytorium i taką, która używa repozytorium.

Obraz05

Zaczniesz od utworzenia stron internetowych, na których kontrolka ObjectDataSource jest powiązana bezpośrednio z repozytorium, ponieważ wykonuje tylko podstawowe zadania dostępu do danych. W następnym samouczku utworzysz klasę logiki biznesowej z logiką walidacji i powiążesz kontrolkę ObjectDataSource z tą klasą zamiast z klasą repozytorium. Utworzysz również testy jednostkowe dla logiki walidacji. W trzecim samouczku z tej serii dodasz do aplikacji funkcję sortowania i filtrowania.

Strony tworzone w tym samouczku współpracują z zestawem Departments jednostek modelu danych utworzonym w serii samouczków Wprowadzenie.

Zrzut ekranu przedstawiający wygląd strony Działy.

Obraz02

Aktualizowanie bazy danych i modelu danych

Rozpoczniesz ten samouczek, wprowadzając dwie zmiany w bazie danych, które wymagają odpowiednich zmian w modelu danych utworzonym w Wprowadzenie za pomocą programu Entity Framework i samouczków Web Forms. W jednym z tych samouczków ręcznie wprowadzono zmiany w projektancie w celu zsynchronizowania modelu danych z bazą danych po zmianie bazy danych. W tym samouczku użyjesz narzędzia Update Model From Database projektanta, aby automatycznie zaktualizować model danych.

Dodawanie relacji do bazy danych

W programie Visual Studio otwórz aplikację internetową Contoso University utworzoną w Wprowadzenie z serii samouczków Entity Framework i Web Forms, a następnie otwórz SchoolDiagram diagram bazy danych.

Jeśli przyjrzysz Department się tabeli na diagramie bazy danych, zobaczysz, że zawiera ona kolumnę Administrator . Ta kolumna jest kluczem Person obcym do tabeli, ale żadna relacja klucza obcego nie jest zdefiniowana w bazie danych. Należy utworzyć relację i zaktualizować model danych, aby program Entity Framework mógł automatycznie obsłużyć tę relację.

Na diagramie bazy danych kliknij prawym przyciskiem myszy tabelę Department , a następnie wybierz pozycję Relacje.

Obraz80

W polu Relacje klucza obcego kliknij przycisk Dodaj, a następnie kliknij wielokropek dla pozycji Tabele i Specyfikacja kolumn.

Obraz81

W oknie dialogowym Tabele i kolumny ustaw tabelę klucza podstawowego i pole na Person i PersonID, a następnie ustaw tabelę i pole klucza obcego na Department i Administrator. (Gdy to zrobisz, nazwa relacji zmieni się z FK_Department_Department na FK_Department_Person.)

Obraz82

Kliknij przycisk OK w polu Tabele i kolumny , kliknij przycisk Zamknij w polu Relacje klucza obcego i zapisz zmiany. Jeśli zostanie wyświetlony monit o zapisanie Person tabel i Department , kliknij przycisk Tak.

Uwaga

Jeśli usunięto Person wiersze odpowiadające danym, które są już w kolumnie Administrator , nie będzie można zapisać tej zmiany. W takim przypadku użyj edytora tabel w Eksploratorze serwera , aby upewnić się, że Administrator wartość w każdym Department wierszu zawiera identyfikator rekordu, który faktycznie istnieje w Person tabeli.

Po zapisaniu zmiany nie będzie można usunąć wiersza z Person tabeli, jeśli ta osoba jest administratorem działu. W aplikacji produkcyjnej należy podać określony komunikat o błędzie, gdy ograniczenie bazy danych uniemożliwia usunięcie lub określisz kaskadowe usunięcie. Przykład sposobu określania kaskadowego usuwania można znaleźć w temacie The Entity Framework and ASP.NET – Wprowadzenie Part 2 (Struktura Entity Framework i ASP.NET — część 2).

Dodawanie widoku do bazy danych

Na nowej stronie Departments.aspx , którą utworzysz, chcesz podać listę rozwijaną instruktorów z nazwami w formacie "last, first", aby użytkownicy mogli wybrać administratorów działów. Aby to ułatwić, utworzysz widok w bazie danych. Widok będzie składać się tylko z danych wymaganych przez listę rozwijaną: pełną nazwę (poprawnie sformatowaną) i klucz rekordu.

W Eksploratorze serwera rozwiń węzeł School.mdf, kliknij prawym przyciskiem myszy folder Widoki , a następnie wybierz pozycję Dodaj nowy widok.

Obraz06

Kliknij przycisk Zamknij po wyświetleniu okna dialogowego Dodawanie tabeli i wklej następującą instrukcję SQL w okienku SQL:

SELECT        LastName + ',' + FirstName AS FullName, PersonID
FROM          dbo.Person
WHERE        (HireDate IS NOT NULL)

Zapisz widok jako vInstructorName.

Aktualizowanie modelu danych

W folderze DAL otwórz plik SchoolModel.edmx , kliknij prawym przyciskiem myszy powierzchnię projektową, a następnie wybierz pozycję Aktualizuj model z bazy danych.

Obraz07

W oknie dialogowym Wybieranie obiektów bazy danych wybierz kartę Dodaj i wybierz właśnie utworzony widok.

Obraz08

Kliknij przycisk Finish (Zakończ).

W projektancie widać, że narzędzie utworzyło vInstructorName jednostkę i nowe skojarzenie między jednostkami Department i Person .

Obraz13

Uwaga

W oknach Lista danych wyjściowych i błędów może zostać wyświetlony komunikat ostrzegawczy informujący o tym, że narzędzie automatycznie utworzyło klucz podstawowy dla nowego vInstructorName widoku. Jest to oczekiwane zachowanie.

W przypadku odwoływania się do nowej vInstructorName jednostki w kodzie nie chcesz używać konwencji bazy danych prefiksowania do niej małej litery "v". W związku z tym zmienisz nazwę jednostki i jednostki ustawionej w modelu.

Otwórz przeglądarkę modelu. Zostanie wyświetlona vInstructorName lista jako typ jednostki i widok.

Obraz14

W obszarze SchoolModel (nie SchoolModel.Store), kliknij prawym przyciskiem myszy pozycję vInstructorName i wybierz pozycję Właściwości. W oknie Właściwości zmień właściwość Name na "InstructorName" i zmień właściwość Entity Set Name na "InstructorNames".

Obraz15

Zapisz i zamknij model danych, a następnie ponownie skompiluj projekt.

Używanie klasy repozytorium i kontrolki ObjectDataSource

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

using System;
using System.Collections.Generic;
using System.Linq;
using ContosoUniversity.DAL;

namespace ContosoUniversity.DAL
{
    public class SchoolRepository : IDisposable
    {
        private SchoolEntities context = new SchoolEntities();

        public IEnumerable<Department> GetDepartments()
        {
            return context.Departments.Include("Person").ToList();
        }

        private bool disposedValue = false;

        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposedValue)
            {
                if (disposing)
                {
                    context.Dispose();
                }
            }
            this.disposedValue = true;
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

    }
}

Ten kod zawiera jedną GetDepartments metodę, która zwraca wszystkie jednostki w Departments zestawie jednostek. Ponieważ wiesz, że będziesz uzyskiwać dostęp do właściwości nawigacji dla każdego zwróconego Person wiersza, określasz chętne ładowanie dla tej właściwości przy użyciu Include metody . Klasa implementuje IDisposable również interfejs, aby upewnić się, że połączenie z bazą danych jest zwalniane po usunięciu obiektu.

Uwaga

Typowym rozwiązaniem jest utworzenie klasy repozytorium dla każdego typu jednostki. W tym samouczku jest używana jedna klasa repozytorium dla wielu typów jednostek. Aby uzyskać więcej informacji na temat wzorca repozytorium, zobacz wpisy w blogu zespołu platformy Entity Framework i blogu Julie Lerman.

Metoda GetDepartments zwraca IEnumerable obiekt, a nie IQueryable obiekt, aby upewnić się, że zwrócona kolekcja będzie można używać nawet po usunięciu samego obiektu repozytorium. Obiekt IQueryable może spowodować dostęp do bazy danych za każdym razem, gdy jest dostępny, ale obiekt repozytorium może zostać usunięty przez czas próby renderowania danych przez kontrolkę ruchu przychodzącego. Można zwrócić inny typ kolekcji, taki jak IList obiekt zamiast IEnumerable obiektu. Jednak zwrócenie IEnumerable obiektu gwarantuje, że można wykonywać typowe zadania przetwarzania listy tylko do odczytu, takie jak foreach pętle i zapytania LINQ, ale nie można dodawać ani usuwać elementów w kolekcji, co może oznaczać, że takie zmiany zostaną utrwalone w bazie danych.

Utwórz stronę Departments.aspx używającą strony wzorcowej Site.Master i dodaj następujący znacznik w kontrolce Content o nazwie Content2:

<h2>Departments</h2>
    <asp:ObjectDataSource ID="DepartmentsObjectDataSource" runat="server" 
        TypeName="ContosoUniversity.DAL.SchoolRepository" 
        DataObjectTypeName="ContosoUniversity.DAL.Department" 
        SelectMethod="GetDepartments" >
    </asp:ObjectDataSource>
    <asp:GridView ID="DepartmentsGridView" runat="server" AutoGenerateColumns="False"
        DataSourceID="DepartmentsObjectDataSource"  >
        <Columns>
            <asp:CommandField ShowEditButton="True" ShowDeleteButton="True"
                ItemStyle-VerticalAlign="Top">
            </asp:CommandField>
            <asp:DynamicField DataField="Name" HeaderText="Name" SortExpression="Name" ItemStyle-VerticalAlign="Top" />
            <asp:DynamicField DataField="Budget" HeaderText="Budget" SortExpression="Budget" ItemStyle-VerticalAlign="Top" />
            <asp:DynamicField DataField="StartDate" HeaderText="Start Date" ItemStyle-VerticalAlign="Top" />
            <asp:TemplateField HeaderText="Administrator" SortExpression="Person.LastName" ItemStyle-VerticalAlign="Top" >
                <ItemTemplate>
                    <asp:Label ID="AdministratorLastNameLabel" runat="server" Text='<%# Eval("Person.LastName") %>'></asp:Label>,
                    <asp:Label ID="AdministratorFirstNameLabel" runat="server" Text='<%# Eval("Person.FirstMidName") %>'></asp:Label>
                </ItemTemplate>
            </asp:TemplateField>
        </Columns>
    </asp:GridView>

Ten znacznik tworzy kontrolkę, która używa właśnie utworzonej ObjectDataSource klasy repozytorium i GridView kontrolki do wyświetlania danych. Kontrolka GridView określa polecenia Edytuj i Usuń , ale nie dodano jeszcze kodu do obsługi tych poleceń.

Kilka kolumn używa DynamicField kontrolek, dzięki czemu można korzystać z funkcji automatycznego formatowania i sprawdzania poprawności danych. Aby te elementy działały, należy wywołać metodę EnableDynamicData w procedurze obsługi zdarzeń Page_Init . (DynamicControl kontrolki nie są używane w Administrator polu, ponieważ nie działają z właściwościami nawigacji).

Atrybuty Vertical-Align="Top" staną się ważne później po dodaniu kolumny z zagnieżdżonym GridView formantem do siatki.

Otwórz plik Departments.aspx.cs i dodaj następującą using instrukcję:

using ContosoUniversity.DAL;

Następnie dodaj następującą procedurę obsługi zdarzenia strony Init :

protected void Page_Init(object sender, EventArgs e)
{
    DepartmentsGridView.EnableDynamicData(typeof(Department));
}

W folderze DAL utwórz nowy plik klasy o nazwie Department.cs i zastąp istniejący kod następującym kodem:

using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace ContosoUniversity.DAL
{
    [MetadataType(typeof(DepartmentMetaData))]
    public partial class Department
    {
    }

    public class DepartmentMetaData
    {
        [DataType(DataType.Currency)]
        [Range(0, 1000000, ErrorMessage = "Budget must be less than $1,000,000.00")]
        public Decimal Budget { get; set; }

        [DisplayFormat(DataFormatString="{0:d}",ApplyFormatInEditMode=true)]
        public DateTime StartDate { get; set; }

    }
}

Ten kod dodaje metadane do modelu danych. Określa, że Budget właściwość Department jednostki faktycznie reprezentuje walutę, chociaż jej typ danych to Decimal, i określa, że wartość musi należeć do zakresu od 0 do 1000 000,000,000 USD. Określa również, że StartDate właściwość powinna być sformatowana jako data w formacie mm/dd/rrrr.

Uruchom stronę Departments.aspx .

Zrzut ekranu przedstawiający stronę Działy po uruchomieniu.

Zwróć uwagę, że chociaż nie określono ciągu formatu w znaczniku strony Departments.aspx dla kolumn Budżet lub Data rozpoczęcia , domyślne formatowanie waluty i daty zostało zastosowane do nich przez DynamicField kontrolki, przy użyciu metadanych podanych w pliku Department.cs .

Dodawanie funkcji wstawiania i usuwania

Otwórz plik SchoolRepository.cs, dodaj następujący kod, aby utworzyć metodę Insert i metodę Delete . Kod zawiera również metodę o nazwie GenerateDepartmentID , która oblicza następną dostępną wartość klucza rekordu do użycia przez metodę Insert . Jest to wymagane, ponieważ baza danych nie jest skonfigurowana do obliczania tej wartości automatycznie dla Department tabeli.

public void InsertDepartment(Department department)
{
    try
    {
        department.DepartmentID = GenerateDepartmentID();
        context.Departments.AddObject(department);
        context.SaveChanges();
    }
    catch (Exception ex)
    {
        //Include catch blocks for specific exceptions first,
        //and handle or log the error as appropriate in each.
        //Include a generic catch block like this one last.
        throw ex;
    }
}

public void DeleteDepartment(Department department)
{
    try
    {
        context.Departments.Attach(department);
        context.Departments.DeleteObject(department);
        context.SaveChanges();
    }
    catch (Exception ex)
    {
        //Include catch blocks for specific exceptions first,
        //and handle or log the error as appropriate in each.
        //Include a generic catch block like this one last.
        throw ex;
    }
}

private Int32 GenerateDepartmentID()
{
    Int32 maxDepartmentID = 0;
    var department = (from d in GetDepartments()
                      orderby d.DepartmentID descending
                      select d).FirstOrDefault();
    if (department != null)
    {
        maxDepartmentID = department.DepartmentID + 1;
    }
    return maxDepartmentID;
}

Metoda Attach

Metoda DeleteDepartment wywołuje metodę Attach w celu ponownego ustanowienia łącza przechowywanego w menedżerze stanu obiektu kontekstu obiektu między jednostką w pamięci a wierszem bazy danych, który reprezentuje. Musi się to zdarzyć przed wywołaniem SaveChanges metody .

Kontekst obiektu terminu odnosi się do klasy Entity Framework, która pochodzi z klasy używanej ObjectContext do uzyskiwania dostępu do zestawów jednostek i jednostek. W kodzie dla tego projektu klasa nosi nazwę SchoolEntities, a wystąpienie jest zawsze nazwane context. Menedżer stanu obiektu kontekstu obiektu jest klasą pochodzącą z ObjectStateManager klasy . Kontakt obiektu używa menedżera stanu obiektu do przechowywania obiektów jednostek i śledzenia, czy każdy z nich jest zsynchronizowany z odpowiednim wierszem tabeli lub wierszami w bazie danych.

Podczas odczytywania jednostki kontekst obiektu przechowuje go w menedżerze stanu obiektu i śledzi, czy ta reprezentacja obiektu jest zsynchronizowana z bazą danych. Jeśli na przykład zmienisz wartość właściwości, flaga zostanie ustawiona, aby wskazać, że zmieniona właściwość nie jest już zsynchronizowana z bazą danych. Następnie po wywołaniu SaveChanges metody kontekst obiektu wie, co zrobić w bazie danych, ponieważ menedżer stanu obiektu dokładnie wie, co różni się od bieżącego stanu jednostki i stanu bazy danych.

Jednak ten proces zazwyczaj nie działa w aplikacji internetowej, ponieważ wystąpienie kontekstu obiektu, które odczytuje jednostkę wraz ze wszystkimi elementami w menedżerze stanu obiektu, jest usuwane po renderowaniu strony. Wystąpienie kontekstu obiektu, które musi zastosować zmiany, jest nowym wystąpieniem na potrzeby przetwarzania zwrotnego. W przypadku DeleteDepartment metody ObjectDataSource kontrolka ponownie tworzy oryginalną wersję jednostki z wartości w stanie widoku, ale ta ponownie utworzona Department jednostka nie istnieje w menedżerze stanu obiektu. Jeśli wywołasz metodę w tej ponownie utworzonej DeleteObject jednostce, wywołanie zakończy się niepowodzeniem, ponieważ kontekst obiektu nie wie, czy jednostka jest zsynchronizowana z bazą danych. Jednak wywołanie Attach metody powoduje ponowne utworzenie tego samego śledzenia między ponownie utworzoną jednostką a wartościami w bazie danych, która została pierwotnie wykonana automatycznie podczas odczytywania jednostki we wcześniejszym wystąpieniu kontekstu obiektu.

Czasami nie chcesz, aby kontekst obiektu śledził jednostki w menedżerze stanu obiektu i można ustawić flagi, aby zapobiec temu. Przykłady tego przykładu przedstawiono w kolejnych samouczkach z tej serii.

Metoda SaveChanges

Ta prosta klasa repozytorium ilustruje podstawowe zasady wykonywania operacji CRUD. W tym przykładzie metoda jest wywoływana SaveChanges natychmiast po każdej aktualizacji. W aplikacji produkcyjnej możesz wywołać metodę SaveChanges z oddzielnej metody, aby zapewnić większą kontrolę nad aktualizacją bazy danych. (Na końcu następnego samouczka znajdziesz link do białej księgi, która omawia jednostkę wzorca pracy, który jest jednym z podejść do koordynowania powiązanych aktualizacji). Zwróć również uwagę, DeleteDepartment że w przykładzie metoda nie zawiera kodu do obsługi konfliktów współbieżności; kod do wykonania zostanie dodany w późniejszym samouczku w tej serii.

Pobieranie nazw instruktorów w celu wybrania podczas wstawiania

Użytkownicy muszą mieć możliwość wybrania administratora z listy instruktorów na liście rozwijanej podczas tworzenia nowych działów. W związku z tym dodaj następujący kod do pliku SchoolRepository.cs , aby utworzyć metodę pobierania listy instruktorów przy użyciu utworzonego wcześniej widoku:

public IEnumerable<InstructorName> GetInstructorNames()
{
    return context.InstructorNames.OrderBy("it.FullName").ToList();
}

Tworzenie strony do wstawiania działów

Utwórz stronę DepartmentsAdd.aspx używającą strony Site.Master i dodaj następujące znaczniki w kontrolce Content o nazwie Content2:

<h2>Departments</h2>
    <asp:ObjectDataSource ID="DepartmentsObjectDataSource" runat="server" 
        TypeName="ContosoUniversity.DAL.SchoolRepository" DataObjectTypeName="ContosoUniversity.DAL.Department"
        InsertMethod="InsertDepartment" >
    </asp:ObjectDataSource>
    <asp:DetailsView ID="DepartmentsDetailsView" runat="server" 
        DataSourceID="DepartmentsObjectDataSource" AutoGenerateRows="False"
        DefaultMode="Insert" OnItemInserting="DepartmentsDetailsView_ItemInserting">
        <Fields>
            <asp:DynamicField DataField="Name" HeaderText="Name" />
            <asp:DynamicField DataField="Budget" HeaderText="Budget" />
            <asp:DynamicField DataField="StartDate" HeaderText="Start Date" />
            <asp:TemplateField HeaderText="Administrator">
                <InsertItemTemplate>
                    <asp:ObjectDataSource ID="InstructorsObjectDataSource" runat="server" 
                        TypeName="ContosoUniversity.DAL.SchoolRepository" 
                        DataObjectTypeName="ContosoUniversity.DAL.InstructorName"
                        SelectMethod="GetInstructorNames" >
                    </asp:ObjectDataSource>
                    <asp:DropDownList ID="InstructorsDropDownList" runat="server" 
                        DataSourceID="InstructorsObjectDataSource"
                        DataTextField="FullName" DataValueField="PersonID" OnInit="DepartmentsDropDownList_Init">
                    </asp:DropDownList>
                </InsertItemTemplate>
            </asp:TemplateField>
            <asp:CommandField ShowInsertButton="True" />
        </Fields>
    </asp:DetailsView>
   <asp:ValidationSummary ID="DepartmentsValidationSummary" runat="server" 
        ShowSummary="true" DisplayMode="BulletList"  />

Ten znacznik tworzy dwie ObjectDataSource kontrolki, jeden do wstawiania nowych Department jednostek i jeden do pobierania nazw instruktorów dla kontrolki używanej DropDownList do wybierania administratorów działów. Znacznik tworzy kontrolkę DetailsView do wprowadzania nowych działów i określa procedurę obsługi zdarzenia kontrolki ItemInserting , aby można było ustawić wartość klucza obcego Administrator . Na końcu jest kontrolką do wyświetlania ValidationSummary komunikatów o błędach.

Otwórz plik DepartmentsAdd.aspx.cs i dodaj następującą using instrukcję:

using ContosoUniversity.DAL;

Dodaj następującą zmienną i metody klasy:

private DropDownList administratorsDropDownList;

protected void Page_Init(object sender, EventArgs e)
{
    DepartmentsDetailsView.EnableDynamicData(typeof(Department));
}

protected void DepartmentsDropDownList_Init(object sender, EventArgs e)
{
    administratorsDropDownList = sender as DropDownList;
}

protected void DepartmentsDetailsView_ItemInserting(object sender, DetailsViewInsertEventArgs e)
{
    e.Values["Administrator"] = administratorsDropDownList.SelectedValue;
}

Metoda Page_Init umożliwia działanie danych dynamicznych. Procedura obsługi DropDownList zdarzenia kontrolki Init zapisuje odwołanie do kontrolki, a procedura obsługi DetailsView zdarzenia kontrolki Inserting używa tego odwołania, aby uzyskać PersonID wartość wybranego instruktora i zaktualizować Administrator właściwość klucza obcego Department jednostki.

Uruchom stronę, dodaj informacje dla nowego działu, a następnie kliknij link Wstaw .

Obraz04

Wprowadź wartości dla innego nowego działu. Wprowadź liczbę większą niż 1000 000,000 w polu Budżet i kartę do następnego pola. Gwiazdka pojawia się w polu, a jeśli przytrzymaj wskaźnik myszy nad nim, zobaczysz komunikat o błędzie wprowadzony w metadanych dla tego pola.

Obraz03

Kliknij przycisk Wstaw i zobaczysz komunikat o błędzie wyświetlany przez kontrolkę ValidationSummary w dolnej części strony.

Obraz12

Następnie zamknij przeglądarkę i otwórz stronę Departments.aspx . Dodaj możliwość usuwania do strony Departments.aspx , dodając DeleteMethod atrybut do kontrolki ObjectDataSource i DataKeyNames atrybut do kontrolki GridView . Tagi otwierające dla tych kontrolek będą teraz przypominać następujący przykład:

<asp:ObjectDataSource ID="DepartmentsObjectDataSource" runat="server" 
        TypeName="ContosoUniversity.DAL.SchoolRepository" 
        DataObjectTypeName="ContosoUniversity.DAL.Department"
        SelectMethod="GetDepartments" 
        DeleteMethod="DeleteDepartment" >

    <asp:GridView ID="DepartmentsGridView" runat="server" AutoGenerateColumns="False"
        DataSourceID="DepartmentsObjectDataSource" DataKeyNames="DepartmentID" >

Uruchom stronę.

Zrzut ekranu przedstawiający stronę Działy po jej uruchomieniu.

Usuń dział dodany podczas uruchamiania strony DepartmentsAdd.aspx .

Dodawanie funkcji aktualizacji

Otwórz plik SchoolRepository.cs i dodaj następującą Update metodę:

public void UpdateDepartment(Department department, Department origDepartment)
{
    try
    {
        context.Departments.Attach(origDepartment);
        context.ApplyCurrentValues("Departments", department);
        context.SaveChanges();
    }
    catch (Exception ex)
    {
        //Include catch blocks for specific exceptions first,
        //and handle or log the error as appropriate in each.
        //Include a generic catch block like this one last.
        throw ex;
    }
}

Po kliknięciu przycisku Aktualizuj na stronie Departments.aspx kontrolka ObjectDataSource tworzy dwie Department jednostki do przekazania do UpdateDepartment metody. Jeden zawiera oryginalne wartości przechowywane w stanie widoku, a drugi zawiera nowe wartości wprowadzone w kontrolce GridView . Kod w metodzie UpdateDepartment przekazuje Department jednostkę, która ma oryginalne wartości do Attach metody w celu ustanowienia śledzenia między jednostką a elementami w bazie danych. Następnie kod przekazuje Department jednostkę, która ma nowe wartości do ApplyCurrentValues metody . Kontekst obiektu porównuje stare i nowe wartości. Jeśli nowa wartość różni się od starej wartości, kontekst obiektu zmienia wartość właściwości. Następnie SaveChanges metoda aktualizuje tylko zmienione kolumny w bazie danych. (Jeśli jednak funkcja update dla tej jednostki została zamapowana na procedurę składowaną, cały wiersz zostanie zaktualizowany niezależnie od tego, które kolumny zostały zmienione).

Otwórz plik Departments.aspx i dodaj następujące atrybuty do kontrolki DepartmentsObjectDataSource :

  • UpdateMethod="UpdateDepartment"
  • ConflictDetection="CompareAllValues"
    Powoduje to przechowywanie starych wartości w stanie widoku, dzięki czemu można je porównać z nowymi wartościami w metodzie Update .
  • OldValuesParameterFormatString="orig{0}"
    Informuje to kontrolkę, że nazwa oryginalnego parametru wartości to origDepartment .

Znacznik otwierający ObjectDataSource kontrolki przypomina teraz następujący przykład:

<asp:ObjectDataSource ID="DepartmentsObjectDataSource" runat="server" 
        TypeName="ContosoUniversity.DAL.SchoolRepository" 
        DataObjectTypeName="ContosoUniversity.DAL.Department" 
        SelectMethod="GetDepartments" DeleteMethod="DeleteDepartment" 
        UpdateMethod="UpdateDepartment"
        ConflictDetection="CompareAllValues" 
        OldValuesParameterFormatString="orig{0}" >

OnRowUpdating="DepartmentsGridView_RowUpdating" Dodaj atrybut do kontrolkiGridView. Ta funkcja służy do ustawiania Administrator wartości właściwości na podstawie wiersza wybranego przez użytkownika na liście rozwijanej. Tag GridView otwierający przypomina teraz następujący przykład:

<asp:GridView ID="DepartmentsGridView" runat="server" AutoGenerateColumns="False"
        DataSourceID="DepartmentsObjectDataSource" DataKeyNames="DepartmentID"
        OnRowUpdating="DepartmentsGridView_RowUpdating">

Dodaj kontrolkę EditItemTemplate dla kolumny Administrator do kontrolki GridView bezpośrednio po kontrolce ItemTemplate dla tej kolumny:

<EditItemTemplate>
                    <asp:ObjectDataSource ID="InstructorsObjectDataSource" runat="server" DataObjectTypeName="ContosoUniversity.DAL.InstructorName"
                        SelectMethod="GetInstructorNames" TypeName="ContosoUniversity.DAL.SchoolRepository">
                    </asp:ObjectDataSource>
                    <asp:DropDownList ID="InstructorsDropDownList" runat="server" DataSourceID="InstructorsObjectDataSource"
                        SelectedValue='<%# Eval("Administrator")  %>'
                        DataTextField="FullName" DataValueField="PersonID" OnInit="DepartmentsDropDownList_Init" >
                    </asp:DropDownList>
                </EditItemTemplate>

Ta EditItemTemplate kontrolka jest podobna do kontrolki InsertItemTemplate na stronie DepartmentsAdd.aspx . Różnica polega na tym, że początkowa wartość kontrolki jest ustawiana przy użyciu atrybutu SelectedValue .

Przed kontrolką GridView dodaj kontrolkę ValidationSummary tak jak w sekcji DziałyDodaj.aspx .

<asp:ValidationSummary ID="DepartmentsValidationSummary" runat="server" 
        ShowSummary="true" DisplayMode="BulletList"  />

Otwórz plik Departments.aspx.cs i natychmiast po deklaracji częściowej klasy dodaj następujący kod, aby utworzyć pole prywatne w celu odwołania się do kontrolki DropDownList :

private DropDownList administratorsDropDownList;

Następnie dodaj programy obsługi zdarzenia DropDownList kontrolki Init i GridView zdarzenia kontrolki RowUpdating :

protected void DepartmentsDropDownList_Init(object sender, EventArgs e)
{
    administratorsDropDownList = sender as DropDownList;
}

protected void DepartmentsGridView_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
    e.NewValues["Administrator"] = administratorsDropDownList.SelectedValue;
}

Procedura obsługi zdarzenia Init zapisuje odwołanie do kontrolki DropDownList w polu klasy. Procedura obsługi zdarzenia RowUpdating używa odwołania, aby uzyskać wartość wprowadzoną przez użytkownika i zastosować ją do Administrator właściwości Department jednostki.

Użyj strony DepartmentsAdd.aspx , aby dodać nowy dział, a następnie uruchom stronę Departments.aspx i kliknij przycisk Edytuj w dodanym wierszu.

Uwaga

Nie będzie można edytować wierszy, które nie zostały dodane (czyli już w bazie danych) z powodu nieprawidłowych danych w bazie danych; administratorzy wierszy utworzonych za pomocą bazy danych to uczniowie. Jeśli spróbujesz edytować jedną z nich, zostanie wyświetlona strona błędu, która zgłasza błąd, taki jak 'InstructorsDropDownList' has a SelectedValue which is invalid because it does not exist in the list of items.

Obraz10

Jeśli wprowadzisz nieprawidłową kwotę budżetu , a następnie kliknij przycisk Aktualizuj, zobaczysz ten sam komunikat gwiazdki i komunikat o błędzie, który został wyświetlony na stronie Departments.aspx .

Zmień wartość pola lub wybierz innego administratora i kliknij przycisk Aktualizuj. Zostanie wyświetlona zmiana.

Zrzut ekranu przedstawiający stronę Działy.

Spowoduje to ukończenie wprowadzenia do używania kontrolki ObjectDataSource dla podstawowych operacji CRUD (tworzenie, odczytywanie, aktualizowanie, usuwanie) za pomocą platformy Entity Framework. Utworzono prostą aplikację n-warstwową, ale warstwa logiki biznesowej jest nadal ściśle połączona z warstwą dostępu do danych, co komplikuje automatyczne testowanie jednostkowe. W poniższym samouczku zobaczysz, jak zaimplementować wzorzec repozytorium w celu ułatwienia testowania jednostkowego.