Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Autor: Tom Dykstra
Przykładowa aplikacja internetowa Contoso University pokazuje, jak tworzyć aplikacje ASP.NET MVC 4 przy użyciu programu Entity Framework 5 Code First i Visual Studio 2012. Aby uzyskać informacje na temat serii samouczków, zobacz pierwszy samouczek z serii.
Uwaga
Jeśli napotkasz problem, którego nie możesz rozwiązać, pobierz ukończony rozdział i spróbuj odtworzyć problem. Zazwyczaj rozwiązanie problemu można znaleźć, porównując kod z ukończonym kodem. Aby uzyskać informacje o niektórych typowych błędach i sposobach ich rozwiązywania, zobacz Błędy i obejścia.
W poprzednim samouczku utworzono aplikację MVC, która przechowuje i wyświetla dane przy użyciu programu Entity Framework i programu SQL Server LocalDB. W tym samouczku zapoznasz się i dostosujesz kod CRUD (tworzenie, odczytywanie, aktualizowanie i usuwanie), który szkielet MVC tworzy automatycznie w kontrolerach i widokach.
Uwaga
Typowym rozwiązaniem jest zaimplementowanie wzorca repozytorium w celu utworzenia warstwy abstrakcji między kontrolerem a warstwą dostępu do danych. Aby zachować prostotę tych samouczków, nie zaimplementujesz repozytorium do późniejszego samouczka z tej serii.
W tym samouczku utworzysz następujące strony internetowe:




Tworzenie strony szczegółów
Kod szkieletu strony Uczniowie Index pominął Enrollments właściwość, ponieważ ta właściwość zawiera kolekcję. Details Na stronie zostanie wyświetlona zawartość kolekcji w tabeli HTML.
W obszarze Controllers\StudentController.cs metoda akcji widoku Details używa Find metody do pobrania pojedynczej Student jednostki.
public ActionResult Details(int id = 0)
{
Student student = db.Students.Find(id);
if (student == null)
{
return HttpNotFound();
}
return View(student);
}
Wartość klucza jest przekazywana do metody jako parametru id i pochodzi z danych trasy w hiperlinku Szczegóły na stronie Indeks.
Otwórz plik Views\Student\Details.cshtml. Każde pole jest wyświetlane przy użyciu
DisplayForpomocnika, jak pokazano w poniższym przykładzie:<div class="display-label"> @Html.DisplayNameFor(model => model.LastName) </div> <div class="display-field"> @Html.DisplayFor(model => model.LastName) </div>EnrollmentDatePo polu i bezpośrednio przed tagiem zamykającymfieldsetdodaj kod, aby wyświetlić listę rejestracji, jak pokazano w poniższym przykładzie:<div class="display-label"> @Html.LabelFor(model => model.Enrollments) </div> <div class="display-field"> <table> <tr> <th>Course Title</th> <th>Grade</th> </tr> @foreach (var item in Model.Enrollments) { <tr> <td> @Html.DisplayFor(modelItem => item.Course.Title) </td> <td> @Html.DisplayFor(modelItem => item.Grade) </td> </tr> } </table> </div> </fieldset> <p> @Html.ActionLink("Edit", "Edit", new { id=Model.StudentID }) | @Html.ActionLink("Back to List", "Index") </p>Ten kod przechodzi przez jednostki we
Enrollmentswłaściwości nawigacji. Dla każdejEnrollmentjednostki we właściwości wyświetla tytuł kursu i ocenę. Tytuł kursu jest pobierany zCoursejednostki przechowywanejCoursewe właściwościEnrollmentsnawigacji jednostki. Wszystkie te dane są pobierane z bazy danych automatycznie, gdy są potrzebne. (Innymi słowy, używasz tutaj leniwego ładowania. Nie określono chętnegoCoursesładowania dla właściwości nawigacji, więc przy pierwszej próbie uzyskania dostępu do tej właściwości zapytanie jest wysyłane do bazy danych w celu pobrania danych. Więcej informacji na temat leniwego ładowania i chętnego ładowania można przeczytać w samouczku Reading Related Data (Odczytywanie powiązanych danych) w dalszej części tej serii).Uruchom stronę, wybierając kartę Uczniowie i klikając link Szczegóły dla Alexandra Carsona. Zostanie wyświetlona lista kursów i ocen dla wybranego ucznia:

Aktualizowanie strony tworzenia
W controllers\StudentController.cs zastąp
HttpPost``Createmetodętry-catchakcji następującym kodem, aby dodać blok i atrybut Bind do metody szkieletowej:[HttpPost] [ValidateAntiForgeryToken] public ActionResult Create( [Bind(Include = "LastName, FirstMidName, EnrollmentDate")] Student student) { try { if (ModelState.IsValid) { db.Students.Add(student); db.SaveChanges(); return RedirectToAction("Index"); } } catch (DataException /* dex */) { //Log the error (uncomment dex variable name after DataException and add a line here to write a log. ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator."); } return View(student); }Ten kod dodaje
Studentjednostkę utworzoną przez powiązanie modelu MVC ASP.NET z zestawemStudentsjednostek, a następnie zapisuje zmiany w bazie danych. (Binder modelu odnosi się do ASP.NET funkcji MVC, która ułatwia pracę z danymi przesłanymi przez formularz; binder modelu konwertuje opublikowane wartości formularza na typy CLR i przekazuje je do metody akcji w parametrach. W tym przypadku binder modelu tworzy wystąpienieStudentjednostki za pomocą wartości właściwości zFormkolekcji.Atrybut
ValidateAntiForgeryTokenpomaga zapobiegać atakom fałszerzmu żądań między witrynami .
> [!WARNING]
> Security - The `Bind` attribute is added to protect against *over-posting*. For example, suppose the `Student` entity includes a `Secret` property that you don't want this web page to update.
>
> [!code-csharp[Main](implementing-basic-crud-functionality-with-the-entity-framework-in-asp-net-mvc-application/samples/sample5.cs?highlight=7)]
>
> Even if you don't have a `Secret` field on the web page, a hacker could use a tool such as [fiddler](http://fiddler2.com/home), or write some JavaScript, to post a `Secret` form value. Without the [Bind](https://msdn.microsoft.com/library/system.web.mvc.bindattribute(v=vs.108).aspx) attribute limiting the fields that the model binder uses when it creates a `Student` instance*,* the model binder would pick up that `Secret` form value and use it to update the `Student` entity instance. Then whatever value the hacker specified for the `Secret` form field would be updated in your database. The following image shows the fiddler tool adding the `Secret` field (with the value "OverPost") to the posted form values.
>
> 
>
> The value "OverPost" would then be successfully added to the `Secret` property of the inserted row, although you never intended that the web page be able to update that property.
>
> It's a security best practice to use the `Include` parameter with the `Bind` attribute to *allowed attributes* fields. It's also possible to use the `Exclude` parameter to *blocked attributes* fields you want to exclude. The reason `Include` is more secure is that when you add a new property to the entity, the new field is not automatically protected by an `Exclude` list.
>
> Another alternative approach, and one preferred by many, is to use only view models with model binding. The view model contains only the properties you want to bind. Once the MVC model binder has finished, you copy the view model properties to the entity instance.
Other than the `Bind` attribute, the `try-catch` block is the only change you've made to the scaffolded code. If an exception that derives from [DataException](https://msdn.microsoft.com/library/system.data.dataexception.aspx) is caught while the changes are being saved, a generic error message is displayed. [DataException](https://msdn.microsoft.com/library/system.data.dataexception.aspx) exceptions are sometimes caused by something external to the application rather than a programming error, so the user is advised to try again. Although not implemented in this sample, a production quality application would log the exception (and non-null inner exceptions ) with a logging mechanism such as [ELMAH](https://code.google.com/p/elmah/).
The code in *Views\Student\Create.cshtml* is similar to what you saw in *Details.cshtml*, except that `EditorFor` and `ValidationMessageFor` helpers are used for each field instead of `DisplayFor`. The following example shows the relevant code:
[!code-cshtml[Main](implementing-basic-crud-functionality-with-the-entity-framework-in-asp-net-mvc-application/samples/sample6.cshtml)]
*Create.cshtml* also includes `@Html.AntiForgeryToken()`, which works with the `ValidateAntiForgeryToken` attribute in the controller to help prevent [cross-site request forgery](../../security/xsrfcsrf-prevention-in-aspnet-mvc-and-web-pages.md) attacks.
No changes are required in *Create.cshtml*.
Uruchom stronę, wybierając kartę Uczniowie i klikając pozycję Utwórz nowy.

Niektóre sprawdzanie poprawności danych działa domyślnie. Wprowadź nazwy i nieprawidłową datę, a następnie kliknij przycisk Utwórz , aby wyświetlić komunikat o błędzie.

Poniższy wyróżniony kod przedstawia sprawdzanie poprawności modelu.
[HttpPost] [ValidateAntiForgeryToken] public ActionResult Create(Student student) { if (ModelState.IsValid) { db.Students.Add(student); db.SaveChanges(); return RedirectToAction("Index"); } return View(student); }Zmień datę na prawidłową wartość, taką jak 1.09.2005, a następnie kliknij przycisk Utwórz , aby zobaczyć, jak nowy student pojawi się na stronie Indeks .

Aktualizowanie strony EDYTUJ POST
W controllers\StudentController.cs HttpGetEdit metoda (ta bez atrybutuHttpPost) używa Find metody do pobrania wybranej Student jednostki, jak pokazano w metodzie .Details Nie musisz zmieniać tej metody.
Zastąp jednak metodę HttpPost Edit akcji następującym kodem, aby dodać try-catch blok i atrybut Bind:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(
[Bind(Include = "StudentID, LastName, FirstMidName, EnrollmentDate")]
Student student)
{
try
{
if (ModelState.IsValid)
{
db.Entry(student).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
}
catch (DataException /* dex */)
{
//Log the error (uncomment dex variable name after DataException and add a line here to write a log.
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
}
return View(student);
}
Ten kod jest podobny do tego, co pokazano w metodzie HttpPost Create . Jednak zamiast dodawać jednostkę utworzoną przez powiązanie modelu do zestawu jednostek, ten kod ustawia flagę na jednostce wskazującej, że została zmieniona. Po wywołaniu metody SaveChanges flaga Zmodyfikowana powoduje, że program Entity Framework tworzy instrukcje SQL w celu zaktualizowania wiersza bazy danych. Wszystkie kolumny wiersza bazy danych zostaną zaktualizowane, w tym te, które użytkownik nie zmienił, a konflikty współbieżności są ignorowane. (Dowiesz się, jak obsługiwać współbieżność w późniejszym samouczku w tej serii).
Stany jednostek oraz metody Attach i SaveChanges
Kontekst bazy danych śledzi, czy jednostki w pamięci są zsynchronizowane z odpowiednimi wierszami w bazie danych, a te informacje określają, co się stanie po wywołaniu SaveChanges metody. Na przykład po przekazaniu nowej jednostki do metody Add stan tej jednostki jest ustawiony na Addedwartość . Następnie po wywołaniu metody SaveChanges kontekst bazy danych wystawia polecenie SQL INSERT .
Jednostka może znajdować się w jednym znastępujących stanów:
Added. Jednostka nie istnieje jeszcze w bazie danych. MetodaSaveChangesmusi wydać instrukcjęINSERT.Unchanged. W tej jednostceSaveChangesnie trzeba nic robić przy użyciu metody . Podczas odczytywania jednostki z bazy danych jednostka zaczyna się od tego stanu.Modified. Niektóre lub wszystkie wartości właściwości jednostki zostały zmodyfikowane. MetodaSaveChangesmusi wydać instrukcjęUPDATE.Deleted. Jednostka została oznaczona do usunięcia. MetodaSaveChangesmusi wydać instrukcjęDELETE.Detached. Jednostka nie jest śledzona przez kontekst bazy danych.
W aplikacji klasycznej zmiany stanu są zwykle ustawiane automatycznie. W typie aplikacji klasycznej odczytujesz jednostkę i wprowadzasz zmiany w niektórych jej wartościach właściwości. Powoduje to automatyczne zmianę stanu jednostki na Modified. Następnie po wywołaniu SaveChangesmetody program Entity Framework generuje instrukcję SQL UPDATE , która aktualizuje tylko rzeczywiste właściwości, które uległy zmianie.
Rozłączony charakter aplikacji internetowych nie zezwala na tę sekwencję ciągłą. Obiekt DbContext odczytujący jednostkę jest usuwany po renderowaniu strony. Po wywołaniu HttpPost Edit metody akcji zostanie wykonane nowe żądanie i masz nowe wystąpienie obiektu DbContext, dlatego należy ręcznie ustawić stan jednostki na Modified. Wartość Następnie po wywołaniu SaveChangesprogramu Entity Framework aktualizuje wszystkie kolumny wiersza bazy danych, ponieważ kontekst nie ma możliwości poznania, które właściwości zostały zmienione.
Jeśli chcesz, aby instrukcja SQL Update aktualizowała tylko pola, które użytkownik rzeczywiście zmienił, możesz zapisać oryginalne wartości w jakiś sposób (na przykład ukryte pola), aby były dostępne po wywołaniu HttpPost Edit metody. Następnie możesz utworzyć jednostkę przy użyciu oryginalnych wartości, wywołać Attach metodę Student z tą oryginalną wersją jednostki, zaktualizować wartości jednostki do nowych wartości, a następnie wywołać metodę SaveChanges. Aby uzyskać więcej informacji, zobacz Stany jednostki i SaveChanges i Dane lokalne w Centrum deweloperów danych MSDN.
Kod w pliku Views\Student\Edit.cshtml jest podobny do tego, co zostało wyświetlone w pliku Create.cshtml i nie są wymagane żadne zmiany.
Uruchom stronę, wybierając kartę Uczniowie , a następnie klikając hiperlink Edytuj .

Zmień niektóre dane i kliknij przycisk Zapisz. Zmienione dane są widoczne na stronie Indeks.

Aktualizowanie strony usuwania
W obszarze Controllers\StudentController.cs kod szablonu metody HttpGet Delete używa Find metody do pobrania wybranej Student jednostki, jak pokazano w metodach Details i Edit . Jednak aby zaimplementować niestandardowy komunikat o błędzie, gdy wywołanie zakończy się SaveChanges niepowodzeniem, do tej metody dodasz pewne funkcje i odpowiadający jej widok.
Jak pokazano na potrzeby operacji aktualizacji i tworzenia, operacje usuwania wymagają dwóch metod akcji. Metoda wywoływana w odpowiedzi na żądanie GET wyświetla widok, który daje użytkownikowi szansę zatwierdzenia lub anulowania operacji usuwania. Jeśli użytkownik go zatwierdzi, zostanie utworzone żądanie POST. W takim przypadku metoda jest wywoływana, HttpPost Delete a następnie metoda faktycznie wykonuje operację usuwania.
Do metody dodasz try-catch blok, HttpPost Delete aby obsłużyć wszelkie błędy, które mogą wystąpić po zaktualizowaniu bazy danych. Jeśli wystąpi błąd, HttpPost Delete metoda wywołuje metodę HttpGet Delete , przekazując jej parametr wskazujący, że wystąpił błąd. Następnie HttpGet Delete metoda redisplays strony potwierdzenia wraz z komunikatem o błędzie, dając użytkownikowi możliwość anulowania lub próby ponownie.
Zastąp metodę
HttpGetDeleteakcji następującym kodem, który zarządza raportowaniem błędów:public ActionResult Delete(bool? saveChangesError=false, int id = 0) { if (saveChangesError.GetValueOrDefault()) { ViewBag.ErrorMessage = "Delete failed. Try again, and if the problem persists see your system administrator."; } Student student = db.Students.Find(id); if (student == null) { return HttpNotFound(); } return View(student); }Ten kod akceptuje opcjonalny parametr logiczny wskazujący, czy został wywołany po niepowodzeniu zapisywania zmian. Ten parametr występuje
false, gdyHttpGetDeletemetoda jest wywoływana bez wcześniejszego błędu. Gdy jest wywoływana przez metodęHttpPostDeletew odpowiedzi na błąd aktualizacji bazy danych, parametr jesttruei komunikat o błędzie jest przekazywany do widoku.Zastąp metodę
HttpPostDeleteakcji (o nazwieDeleteConfirmed) następującym kodem, który wykonuje rzeczywistą operację usuwania i przechwytuje wszelkie błędy aktualizacji bazy danych.[HttpPost] [ValidateAntiForgeryToken] public ActionResult Delete(int id) { try { Student student = db.Students.Find(id); db.Students.Remove(student); db.SaveChanges(); } catch (DataException/* dex */) { // uncomment dex and log error. return RedirectToAction("Delete", new { id = id, saveChangesError = true }); } return RedirectToAction("Index"); }Ten kod pobiera wybraną jednostkę, a następnie wywołuje metodę Remove , aby ustawić stan jednostki na
Deleted. PoSaveChangeswywołaniu jest generowane polecenie SQLDELETE. Zmieniono również nazwę metody akcji zDeleteConfirmednaDelete. Kod szkieletowy o nazwieHttpPostDeletemetodyDeleteConfirmedw celu nadania metodzieHttpPostunikatowego podpisu. ( ClR wymaga przeciążonych metod, aby mieć różne parametry metody). Teraz, gdy podpisy są unikatowe, możesz trzymać się konwencji MVC i używać tej samej nazwy dlaHttpPostmetod iHttpGetusuwania.Jeśli zwiększenie wydajności w aplikacji o dużej ilości jest priorytetem, można uniknąć niepotrzebnego zapytania SQL w celu pobrania wiersza, zastępując wiersze kodu, które wywołają
Findmetody iRemovenastępującym kodem, jak pokazano w żółtym wyróżnieniu:Student studentToDelete = new Student() { StudentID = id }; db.Entry(studentToDelete).State = EntityState.Deleted;Ten kod tworzy wystąpienie
Studentjednostki przy użyciu tylko wartości klucza podstawowego, a następnie ustawia stan jednostki naDeleted. To wszystko, co wymaga program Entity Framework w celu usunięcia jednostki.Jak wspomniano,
HttpGetDeletemetoda nie usuwa danych. Wykonanie operacji usuwania w odpowiedzi na żądanie GET (lub w tym przypadku wykonanie dowolnej operacji edycji, operacji tworzenia lub dowolnej innej operacji, która zmienia dane) powoduje zagrożenie bezpieczeństwa. Aby uzyskać więcej informacji, zobacz ASP.NET MVC Tip #46 — Nie używaj linków usuwania, ponieważ tworzą zabezpieczeń na blogu Stephena Walthera.W pliku Views\Student\Delete.cshtml dodaj komunikat o błędzie między nagłówkiem
h2a nagłówkiemh3, jak pokazano w poniższym przykładzie:<h2>Delete</h2> <p class="error">@ViewBag.ErrorMessage</p> <h3>Are you sure you want to delete this?</h3>Uruchom stronę, wybierając kartę Uczniowie i klikając hiperlink Usuń :

Kliknij Usuń. Strona Indeks jest wyświetlana bez usuniętego ucznia. (Zobaczysz przykład kodu obsługi błędów w działaniu w pliku Obsługa samouczka współbieżności w dalszej części tej serii).
Upewnienie się, że połączenia z bazą danych nie są otwarte
Aby upewnić się, że połączenia z bazą danych są prawidłowo zamknięte i zasoby, które zostały zwolnione, należy zobaczyć, że wystąpienie kontekstu jest usuwane. Dlatego kod szkieletowy udostępnia metodę Dispose na końcu StudentController klasy w StudentController.cs, jak pokazano w poniższym przykładzie:
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
Klasa bazowa Controller już implementuje IDisposable interfejs, więc ten kod po prostu dodaje przesłonięcia do Dispose(bool) metody w celu jawnego usunięcia wystąpienia kontekstu.
Podsumowanie
Masz teraz kompletny zestaw stron, które wykonują proste operacje CRUD dla Student jednostek. Użyto pomocników MVC do generowania elementów interfejsu użytkownika dla pól danych. Aby uzyskać więcej informacji na temat pomocników MVC, zobacz Renderowanie formularza przy użyciu pomocników HTML (strona jest dla MVC 3, ale nadal jest odpowiednia dla MVC 4).
W następnym samouczku rozszerzysz funkcjonalność strony Indeks, dodając sortowanie i stronicowanie.
Linki do innych zasobów programu Entity Framework można znaleźć na mapie zawartości dostępu do danych ASP.NET.