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 interfejsuObjectContext
API. Aby uzyskać informacje na temat używania kontrolki EntityDataSource z interfejsemDbContext
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.
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.
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.
W polu Relacje klucza obcego kliknij przycisk Dodaj, a następnie kliknij wielokropek dla pozycji Tabele i Specyfikacja kolumn.
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
.)
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.
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.
W oknie dialogowym Wybieranie obiektów bazy danych wybierz kartę Dodaj i wybierz właśnie utworzony widok.
Kliknij przycisk Finish (Zakończ).
W projektancie widać, że narzędzie utworzyło vInstructorName
jednostkę i nowe skojarzenie między jednostkami Department
i Person
.
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.
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".
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 .
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 .
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.
Kliknij przycisk Wstaw i zobaczysz komunikat o błędzie wyświetlany przez kontrolkę ValidationSummary
w dolnej części strony.
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ę.
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 metodzieUpdate
.OldValuesParameterFormatString="orig{0}"
Informuje to kontrolkę, że nazwa oryginalnego parametru wartości toorigDepartment
.
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.
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.
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.