Samouczek: implementowanie funkcji CRUD — ASP.NET MVC za pomocą polecenia EF Core

W poprzednim samouczku utworzono aplikację MVC, która przechowuje i wyświetla dane przy użyciu programu Entity Framework i 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 i skoncentrować się na sposobie korzystania z samego programu Entity Framework, nie używają repozytoriów. Aby uzyskać informacje na temat repozytoriów z platformą EF, zobacz ostatni samouczek z tej serii.

W tym samouczku zostały wykonane następujące czynności:

  • Dostosowywanie strony Szczegóły
  • Aktualizowanie strony Tworzenie
  • Aktualizowanie strony Edytuj
  • Aktualizowanie strony Usuwanie
  • Zamykanie połączeń bazy danych

Wymagania wstępne

Dostosowywanie strony Szczegóły

Kod szkieletu strony Indeks uczniów opuścił Enrollments właściwość, ponieważ ta właściwość zawiera kolekcję. Na stronie Szczegóły zostanie wyświetlona zawartość kolekcji w tabeli HTML.

W Controllers/StudentsController.cssystemie metoda akcji widoku Szczegóły używa FirstOrDefaultAsync metody do pobrania pojedynczej Student jednostki. Dodaj kod, który wywołuje metodę Include. ThenInclude, i AsNoTracking metody, jak pokazano w poniższym wyróżnionym kodzie.

public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var student = await _context.Students
        .Include(s => s.Enrollments)
            .ThenInclude(e => e.Course)
        .AsNoTracking()
        .FirstOrDefaultAsync(m => m.ID == id);

    if (student == null)
    {
        return NotFound();
    }

    return View(student);
}

Metody Include i ThenInclude powodują załadowanie Student.Enrollments właściwości nawigacji przez kontekst i w ramach każdej rejestracji Enrollment.Course właściwości nawigacji. Więcej informacji na temat tych metod znajdziesz w samouczku dotyczącym odczytu powiązanych danych .

Metoda AsNoTracking zwiększa wydajność w scenariuszach, w których zwrócone jednostki nie zostaną zaktualizowane w okresie istnienia bieżącego kontekstu. Więcej informacji AsNoTracking znajdziesz na końcu tego samouczka.

Dane trasy

Wartość klucza przekazywana do Details metody pochodzi z danych trasy. Dane trasy to dane, które powiązanie modelu można znaleźć w segmencie adresu URL. Na przykład trasa domyślna określa segmenty kontrolerów, akcji i identyfikatorów:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
});

W poniższym adresie URL domyślna trasa mapuje instruktora jako kontrolera, Indeks jako akcję i 1 jako identyfikator; są to wartości danych tras.

http://localhost:1230/Instructor/Index/1?courseID=2021

Ostatnia część adresu URL ("?courseID=2021") jest wartością ciągu zapytania. Powiązanie modelu przekaże również wartość identyfikatora do parametru Index metody id , jeśli przekażesz ją jako wartość ciągu zapytania:

http://localhost:1230/Instructor/Index?id=1&CourseID=2021

Na stronie Indeks adresy URL hiperłącza są tworzone przez instrukcje pomocnika tagów Razor w widoku. W poniższym Razor kodzie id parametr jest zgodny z trasą domyślną, dlatego id jest dodawany do danych trasy.

<a asp-action="Edit" asp-route-id="@item.ID">Edit</a>

Spowoduje to wygenerowanie następującego kodu HTML, gdy item.ID ma wartość 6:

<a href="/Students/Edit/6">Edit</a>

W poniższym Razor kodzie studentID parametr nie jest zgodny z parametrem w trasie domyślnej, dlatego jest dodawany jako ciąg zapytania.

<a asp-action="Edit" asp-route-studentID="@item.ID">Edit</a>

Spowoduje to wygenerowanie następującego kodu HTML, gdy item.ID ma wartość 6:

<a href="/Students/Edit?studentID=6">Edit</a>

Aby uzyskać więcej informacji na temat pomocników tagów, zobacz Pomocnicy tagów w programie ASP.NET Core.

Dodawanie rejestracji do widoku Szczegóły

Otwórz Views/Students/Details.cshtml. Każde pole jest wyświetlane przy użyciu pomocników DisplayNameFor i DisplayFor , jak pokazano w poniższym przykładzie:

<dt class="col-sm-2">
    @Html.DisplayNameFor(model => model.LastName)
</dt>
<dd class="col-sm-10">
    @Html.DisplayFor(model => model.LastName)
</dd>

Po ostatnim polu i bezpośrednio przed tagiem zamykającym </dl> dodaj następujący kod, aby wyświetlić listę rejestracji:

<dt class="col-sm-2">
    @Html.DisplayNameFor(model => model.Enrollments)
</dt>
<dd class="col-sm-10">
    <table class="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>
</dd>

Jeśli wcięcie kodu jest nieprawidłowe po wklejeniu kodu, naciśnij klawisze CTRL-K-D, aby go poprawić.

Ten kod przechodzi przez jednostki we Enrollments właściwości nawigacji. Dla każdej rejestracji wyświetla tytuł kursu i ocenę. Tytuł kursu jest pobierany z jednostki Course przechowywanej Course we właściwości nawigacji jednostki Enrollments.

Uruchom aplikację, wybierz kartę Uczniowie , a następnie kliknij link Szczegóły dla ucznia. Zostanie wyświetlona lista kursów i ocen dla wybranego ucznia:

Student Details page

Aktualizowanie strony Tworzenie

W StudentsController.cspliku zmodyfikuj metodę HttpPost Create , dodając blok try-catch i usuwając identyfikator z atrybutu Bind .

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(
    [Bind("EnrollmentDate,FirstMidName,LastName")] Student student)
{
    try
    {
        if (ModelState.IsValid)
        {
            _context.Add(student);
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }
    }
    catch (DbUpdateException /* ex */)
    {
        //Log the error (uncomment ex variable name and 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 jednostkę Student utworzoną przez powiązanie modelu MVC platformy ASP.NET Core z zestawem jednostek Students, a następnie zapisuje zmiany w bazie danych. (Binder modelu odwołuje się do funkcji ASP.NET Core 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ąpienie jednostki Student przy użyciu wartości właściwości z kolekcji Formularz).

Usunięto ID z atrybutu Bind , ponieważ identyfikator jest wartością klucza podstawowego, którą program SQL Server ustawi automatycznie po wstawieniu wiersza. Dane wejściowe od użytkownika nie ustawiają wartości identyfikatora.

Poza atrybutem Bind blok try-catch jest jedyną zmianą wprowadzoną w kodzie szkieletowym. Jeśli podczas zapisywania zmian zostanie przechwycony DbUpdateException wyjątek, z którego pochodzą zmiany, zostanie wyświetlony ogólny komunikat o błędzie. DbUpdateException wyjątki są czasami spowodowane przez coś zewnętrznego dla aplikacji, a nie błąd programowania, dlatego zaleca się, aby użytkownik próbował ponownie. Chociaż nie zaimplementowano w tym przykładzie, aplikacja jakości produkcyjnej rejestruje wyjątek. Aby uzyskać więcej informacji, zobacz sekcję Dziennik dla szczegółowych informacji w temacie Monitorowanie i telemetria (tworzenie rzeczywistych aplikacji w chmurze za pomocą platformy Azure).

Atrybut ValidateAntiForgeryToken pomaga zapobiegać atakom fałszerzowania żądań między witrynami (CSRF). Token jest automatycznie wstrzykiwany do widoku przez element FormTagHelper i jest dołączany po przesłaniu formularza przez użytkownika. Token jest weryfikowany przez ValidateAntiForgeryToken atrybut . Aby uzyskać więcej informacji, zobacz Zapobieganie atakom z fałszowaniem żądań międzywitrynowych (XSRF/CSRF) na platformie ASP.NET Core.

Uwaga dotycząca zabezpieczeń dotycząca przesłaniania

Atrybut Bind , który zawiera kod szkieletowy w metodzie Create , jest jednym ze sposobów ochrony przed przesłaniem w scenariuszach tworzenia. Załóżmy na przykład, że jednostka Student zawiera Secret właściwość, której nie chcesz ustawić na tej stronie sieci Web.

public class Student
{
    public int ID { get; set; }
    public string LastName { get; set; }
    public string FirstMidName { get; set; }
    public DateTime EnrollmentDate { get; set; }
    public string Secret { get; set; }
}

Nawet jeśli nie masz Secret pola na stronie internetowej, haker może użyć narzędzia takiego jak Fiddler lub napisać jakiś kod JavaScript, aby opublikować Secret wartość formularza. Bind Bez ograniczania pól używanych przez powiązanie modelu podczas tworzenia wystąpienia studenta powiązanie modelu spowoduje odebranie tej Secret wartości formularza i użycie jej do utworzenia wystąpienia jednostki Student. Następnie dowolna wartość określona przez hakera Secret dla pola formularza zostanie zaktualizowana w bazie danych. Na poniższej ilustracji przedstawiono narzędzie Fiddler dodające Secret pole (z wartością "OverPost") do opublikowanych wartości formularza.

Fiddler adding Secret field

Następnie wartość "OverPost" zostanie pomyślnie dodana do Secret właściwości wstawionego wiersza, chociaż nigdy nie zamierzano ustawiać tej właściwości na stronie internetowej.

Możesz zapobiec przesłaniu w scenariuszach edycji, odczytując jednostkę z bazy danych, a następnie wywołując metodę TryUpdateModel, przekazując jawnie dozwoloną listę właściwości. Jest to metoda używana w tych samouczkach.

Alternatywnym sposobem zapobiegania przesłanianiu preferowanym przez wielu deweloperów jest użycie modeli wyświetlania, a nie klas jednostek z powiązaniem modelu. Uwzględnij tylko właściwości, które chcesz zaktualizować w modelu widoku. Po zakończeniu powiązania modelu MVC skopiuj właściwości modelu widoku do wystąpienia jednostki, opcjonalnie przy użyciu narzędzia takiego jak AutoMapper. Użyj _context.Entry polecenia w wystąpieniu jednostki, aby ustawić jego stan na Unchangedwartość , a następnie ustaw Property("PropertyName").IsModified wartość true dla każdej właściwości jednostki uwzględnionej w modelu widoku. Ta metoda działa zarówno w scenariuszach edycji, jak i tworzenia.

Testowanie strony Tworzenie

Kod w programie Views/Students/Create.cshtml używa labelpomocników tagów , inputi span (dla komunikatów weryfikacji) dla każdego pola.

Uruchom aplikację, wybierz kartę Uczniowie , a następnie kliknij pozycję Utwórz nowy.

Wprowadź nazwy i datę. Spróbuj wprowadzić nieprawidłową datę, jeśli przeglądarka pozwoli ci to zrobić. (Niektóre przeglądarki wymuszają użycie selektora dat). Następnie kliknij przycisk Utwórz , aby wyświetlić komunikat o błędzie.

Date validation error

Jest to weryfikacja po stronie serwera, która jest domyślnie uzyskiwana; W późniejszym samouczku dowiesz się, jak dodać atrybuty, które będą generować kod na potrzeby weryfikacji po stronie klienta. Poniższy wyróżniony kod przedstawia sprawdzanie poprawności modelu w metodzie Create .

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(
    [Bind("EnrollmentDate,FirstMidName,LastName")] Student student)
{
    try
    {
        if (ModelState.IsValid)
        {
            _context.Add(student);
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }
    }
    catch (DbUpdateException /* ex */)
    {
        //Log the error (uncomment ex variable name and write a log.
        ModelState.AddModelError("", "Unable to save changes. " +
            "Try again, and if the problem persists " +
            "see your system administrator.");
    }
    return View(student);
}

Zmień datę na prawidłową wartość i kliknij przycisk Utwórz , aby wyświetlić nowego ucznia na stronie Indeks .

Aktualizowanie strony Edytuj

W StudentController.cssystemie metoda HttpGet Edit (ta bez atrybutu HttpPost ) używa FirstOrDefaultAsync metody do pobrania wybranej jednostki Student, jak pokazano w metodzie Details . Nie musisz zmieniać tej metody.

Zastąp metodę akcji HttpPost Edit następującym kodem.

[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditPost(int? id)
{
    if (id == null)
    {
        return NotFound();
    }
    var studentToUpdate = await _context.Students.FirstOrDefaultAsync(s => s.ID == id);
    if (await TryUpdateModelAsync<Student>(
        studentToUpdate,
        "",
        s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate))
    {
        try
        {
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }
        catch (DbUpdateException /* ex */)
        {
            //Log the error (uncomment ex variable name and write a log.)
            ModelState.AddModelError("", "Unable to save changes. " +
                "Try again, and if the problem persists, " +
                "see your system administrator.");
        }
    }
    return View(studentToUpdate);
}

Te zmiany implementują najlepsze rozwiązanie w zakresie zabezpieczeń, aby zapobiec przesłaniu. Szkielet wygenerował Bind atrybut i dodał jednostkę utworzoną przez powiązanie modelu do zestawu jednostek z flagą Modified . Ten kod nie jest zalecany w wielu scenariuszach, ponieważ Bind atrybut usuwa wszystkie istniejące dane w polach, które nie są wymienione w parametrze Include .

Nowy kod odczytuje istniejącą jednostkę i wywołuje metodę TryUpdateModel aktualizowania pól w pobranej jednostce na podstawie danych wejściowych użytkownika w opublikowanych danych formularza. Automatyczne śledzenie zmian w programie Entity Framework ustawia flagę Modified na polach, które są zmieniane przez dane wejściowe formularza. Po wywołaniu SaveChanges metody program Entity Framework tworzy instrukcje SQL w celu zaktualizowania wiersza bazy danych. Konflikty współbieżności są ignorowane i tylko kolumny tabeli zaktualizowane przez użytkownika są aktualizowane w bazie danych. (W późniejszym samouczku pokazano, jak obsługiwać konflikty współbieżności).

Najlepszym rozwiązaniem w celu zapobiegania przesłaniu pól, które mają być aktualizowane przez stronę Edytuj , są deklarowane w parametrach TryUpdateModel . (Pusty ciąg poprzedzający listę pól na liście parametrów jest prefiksem używanym z nazwami pól formularza). Obecnie nie ma dodatkowych pól, które są chronione, ale wyświetlanie listy pól, które mają zostać powiązane z powiązaniem modelu, gwarantuje, że w przypadku dodawania pól do modelu danych są one automatycznie chronione do momentu jawnego dodania ich w tym miejscu.

W wyniku tych zmian sygnatura metody HttpPost Edit jest taka sama jak metoda HttpGet Edit , dlatego zmieniono nazwę metody EditPost.

Alternatywny kod edycji HttpPost: tworzenie i dołączanie

Zalecany kod edycji httpPost gwarantuje, że tylko zmienione kolumny są aktualizowane i zachowują dane we właściwościach, które nie są uwzględniane w powiązaniu modelu. Jednak podejście typu "pierwszy do odczytu" wymaga dodatkowego odczytu bazy danych i może spowodować bardziej złożony kod do obsługi konfliktów współbieżności. Alternatywą jest dołączenie jednostki utworzonej przez powiązanie modelu do kontekstu ef i oznaczenie jej jako zmodyfikowanej. (Nie aktualizuj projektu przy użyciu tego kodu, pokazano tylko, aby zilustrować opcjonalne podejście).

public async Task<IActionResult> Edit(int id, [Bind("ID,EnrollmentDate,FirstMidName,LastName")] Student student)
{
    if (id != student.ID)
    {
        return NotFound();
    }
    if (ModelState.IsValid)
    {
        try
        {
            _context.Update(student);
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }
        catch (DbUpdateException /* ex */)
        {
            //Log the error (uncomment ex variable name and write a log.)
            ModelState.AddModelError("", "Unable to save changes. " +
                "Try again, and if the problem persists, " +
                "see your system administrator.");
        }
    }
    return View(student);
}

Tego podejścia można użyć, gdy interfejs użytkownika strony internetowej zawiera wszystkie pola w jednostce i może je zaktualizować.

Kod szkieletowy używa podejścia create-and-attach, ale przechwytuje DbUpdateConcurrencyException tylko wyjątki i zwraca kody błędów 404. Pokazany przykład przechwytuje wyjątek aktualizacji bazy danych i wyświetla komunikat o błędzie.

Stany jednostek

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 Add metody stan tej jednostki ma wartość Added. Następnie po wywołaniu SaveChanges metody kontekst bazy danych wystawia polecenie SQL INSERT.

Jednostka może znajdować się w jednym z następujących stanów:

  • Added. Jednostka nie istnieje jeszcze w bazie danych. Metoda SaveChanges wystawia instrukcję INSERT.

  • Unchanged. W tej jednostce SaveChanges nie 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. Metoda SaveChanges wystawia instrukcję UPDATE.

  • Deleted. Jednostka została oznaczona do usunięcia. Metoda SaveChanges wystawia instrukcję DELETE.

  • Detached. Jednostka nie jest śledzona przez kontekst bazy danych.

W aplikacji klasycznej zmiany stanu są zwykle ustawiane automatycznie. 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.

W aplikacji internetowej element, który początkowo odczytuje jednostkę i wyświetla dane do edycji, DbContext jest usuwany po renderowaniu strony. Po wywołaniu metody akcji HttpPost Edit zostanie wykonane nowe żądanie internetowe i masz nowe wystąpienie .DbContext Jeśli ponownie odczytujesz jednostkę w tym nowym kontekście, symulujesz przetwarzanie pulpitu.

Jeśli jednak nie chcesz wykonywać dodatkowej operacji odczytu, musisz użyć obiektu jednostki utworzonego przez powiązanie modelu. Najprostszym sposobem, aby to zrobić, jest ustawienie stanu jednostki na Zmodyfikowany, tak jak jest wykonywane w alternatywnym kodzie HttpPost Edit pokazanym wcześniej. Następnie po wywołaniu SaveChangesmetody program 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 uniknąć podejścia do odczytu po raz pierwszy, ale chcesz również, aby instrukcja SQL UPDATE aktualizowała tylko pola, które użytkownik rzeczywiście zmienił, kod jest bardziej złożony. Musisz zapisać oryginalne wartości w jakiś sposób (na przykład przy użyciu ukrytych pól), aby były dostępne po wywołaniu metody HttpPost Edit . Następnie możesz utworzyć jednostkę Student przy użyciu oryginalnych wartości, wywołać Attach metodę z tą oryginalną wersją jednostki, zaktualizować wartości jednostki do nowych wartości, a następnie wywołać metodę SaveChanges.

Testowanie strony Edytuj

Uruchom aplikację, wybierz kartę Uczniowie , a następnie kliknij hiperlink Edytuj .

Students edit page

Zmień niektóre dane i kliknij przycisk Zapisz. Zostanie otwarta strona Indeks i zobaczysz zmienione dane.

Aktualizowanie strony Usuwanie

W StudentController.cspliku kod szablonu metody HttpGet Delete używa FirstOrDefaultAsync metody w celu pobrania wybranej jednostki Student, 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 HttpPost Delete jest wywoływana, a następnie ta metoda faktycznie wykonuje operację usuwania.

Do metody HttpPost Delete dodasz blok try-catch, aby obsłużyć wszelkie błędy, które mogą wystąpić podczas aktualizowania bazy danych. Jeśli wystąpi błąd, metoda HttpPost Delete wywołuje metodę HttpGet Delete, przekazując jej parametr wskazujący, że wystąpił błąd. Następnie metoda HttpGet Delete redisplays strony potwierdzenia wraz z komunikatem o błędzie, co daje użytkownikowi możliwość anulowania lub próby ponownie.

Zastąp metodę akcji HttpGet Delete następującym kodem, który zarządza raportowaniem błędów.

public async Task<IActionResult> Delete(int? id, bool? saveChangesError = false)
{
    if (id == null)
    {
        return NotFound();
    }

    var student = await _context.Students
        .AsNoTracking()
        .FirstOrDefaultAsync(m => m.ID == id);
    if (student == null)
    {
        return NotFound();
    }

    if (saveChangesError.GetValueOrDefault())
    {
        ViewData["ErrorMessage"] =
            "Delete failed. Try again, and if the problem persists " +
            "see your system administrator.";
    }

    return View(student);
}

Ten kod akceptuje opcjonalny parametr wskazujący, czy metoda została wywołana po niepowodzeniu zapisywania zmian. Ten parametr ma wartość false, gdy metoda HttpGet Delete jest wywoływana bez wcześniejszego błędu. Gdy jest wywoływana przez metodę HttpPost Delete w odpowiedzi na błąd aktualizacji bazy danych, parametr ma wartość true, a komunikat o błędzie jest przekazywany do widoku.

Podejście typu "pierwszy do odczytu" do usuwania httppost

Zastąp metodę akcji HttpPost Delete (o nazwie DeleteConfirmed) następującym kodem, który wykonuje rzeczywistą operację usuwania i przechwytuje wszelkie błędy aktualizacji bazy danych.

[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
    var student = await _context.Students.FindAsync(id);
    if (student == null)
    {
        return RedirectToAction(nameof(Index));
    }

    try
    {
        _context.Students.Remove(student);
        await _context.SaveChangesAsync();
        return RedirectToAction(nameof(Index));
    }
    catch (DbUpdateException /* ex */)
    {
        //Log the error (uncomment ex variable name and write a log.)
        return RedirectToAction(nameof(Delete), new { id = id, saveChangesError = true });
    }
}

Ten kod pobiera wybraną jednostkę, a następnie wywołuje metodę Remove , aby ustawić stan jednostki na Deleted. Po SaveChanges wywołaniu jest generowane polecenie SQL DELETE.

Metoda tworzenia i dołączania do usuwania httpPost

Jeśli zwiększenie wydajności w aplikacji o dużej ilości jest priorytetem, można uniknąć niepotrzebnego zapytania SQL, tworząc wystąpienie jednostki Student przy użyciu tylko wartości klucza podstawowego, a następnie ustawiając stan jednostki na Deleted. To wszystko, co wymaga program Entity Framework w celu usunięcia jednostki. (Nie umieszczaj tego kodu w projekcie— wystarczy zilustrować alternatywę).

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
    try
    {
        Student studentToDelete = new Student() { ID = id };
        _context.Entry(studentToDelete).State = EntityState.Deleted;
        await _context.SaveChangesAsync();
        return RedirectToAction(nameof(Index));
    }
    catch (DbUpdateException /* ex */)
    {
        //Log the error (uncomment ex variable name and write a log.)
        return RedirectToAction(nameof(Delete), new { id = id, saveChangesError = true });
    }
}

Jeśli jednostka ma powiązane dane, które również powinny zostać usunięte, upewnij się, że w bazie danych skonfigurowano usuwanie kaskadowe. Dzięki temu podejściu do usuwania jednostek program EF może nie pamiętać, że istnieją powiązane jednostki, które mają zostać usunięte.

Aktualizowanie widoku Usuwania

W Views/Student/Delete.cshtmlpliku dodaj komunikat o błędzie między nagłówkiem h2 i nagłówkiem h3, jak pokazano w poniższym przykładzie:

<h2>Delete</h2>
<p class="text-danger">@ViewData["ErrorMessage"]</p>
<h3>Are you sure you want to delete this?</h3>

Uruchom aplikację, wybierz kartę Uczniowie i kliknij hiperlink Usuń :

Delete confirmation page

Kliknij polecenie Usuń. Strona Indeks jest wyświetlana bez usuniętego ucznia. (W samouczku współbieżności zostanie wyświetlony przykład kodu obsługi błędów).

Zamykanie połączeń bazy danych

Aby zwolnić zasoby przechowywane przez połączenie z bazą danych, wystąpienie kontekstu musi zostać usunięte tak szybko, jak to możliwe po zakończeniu pracy z nim. Wbudowana iniekcja zależności ASP.NET Core zajmuje się tym zadaniem.

W Startup.cspliku wywołasz metodę rozszerzenia AddDbContext, aby aprowizować klasę DbContext w kontenerze ASP.NET Core DI. Ta metoda domyślnie ustawia okres istnienia usługi na Scoped wartość . Scoped oznacza, że okres istnienia obiektu kontekstu pokrywa się z czasem życia żądania internetowego, a Dispose metoda zostanie wywołana automatycznie na końcu żądania internetowego.

Obsługa transakcji

Domyślnie program Entity Framework niejawnie implementuje transakcje. W scenariuszach, w których wprowadzasz zmiany w wielu wierszach lub tabelach, a następnie wywołujesz SaveChangesmetodę , program Entity Framework automatycznie upewnia się, że wszystkie zmiany zakończyły się powodzeniem lub wszystkie zakończyły się niepowodzeniem. Jeśli najpierw zostaną wykonane pewne zmiany, a następnie wystąpi błąd, te zmiany zostaną automatycznie wycofane. W przypadku scenariuszy, w których potrzebujesz większej kontroli — na przykład jeśli chcesz uwzględnić operacje wykonywane poza platformą Entity Framework w transakcji — zobacz Transakcje.

Zapytania bez śledzenia

Gdy kontekst bazy danych pobiera wiersze tabeli i tworzy obiekty jednostki, które je reprezentują, domyślnie śledzi, czy jednostki w pamięci są zsynchronizowane z elementami w bazie danych. Dane w pamięci działają jako pamięć podręczna i są używane podczas aktualizowania jednostki. Buforowanie jest często niepotrzebne w aplikacji internetowej, ponieważ wystąpienia kontekstu są zwykle krótkotrwałe (nowy jest tworzony i usuwany dla każdego żądania) oraz kontekst, który odczytuje jednostkę, jest zwykle usuwany przed ponownym użyciu tej jednostki.

Śledzenie obiektów jednostek w pamięci można wyłączyć, wywołując metodę AsNoTracking . Typowe scenariusze, w których warto to zrobić, obejmują następujące czynności:

  • W okresie istnienia kontekstu nie trzeba aktualizować żadnych jednostek i nie trzeba automatycznie ładować właściwości nawigacji za pomocą jednostek pobranych przez oddzielne zapytania. Często te warunki są spełnione w metodach akcji HttpGet kontrolera.

  • Uruchamiasz zapytanie, które pobiera dużą ilość danych, a tylko niewielka część zwróconych danych zostanie zaktualizowana. Może to być bardziej wydajne, aby wyłączyć śledzenie dla dużych zapytań i uruchomić zapytanie później dla kilku jednostek, które muszą zostać zaktualizowane.

  • Chcesz dołączyć jednostkę, aby ją zaktualizować, ale wcześniej pobrano tę samą jednostkę w innym celu. Ponieważ jednostka jest już śledzona przez kontekst bazy danych, nie można dołączyć jednostki, którą chcesz zmienić. Jednym ze sposobów obsługi tej sytuacji jest wywołanie AsNoTracking wcześniejszego zapytania.

Aby uzyskać więcej informacji, zobacz Śledzenie a brak śledzenia.

Uzyskiwanie kodu

Pobierz lub wyświetl ukończoną aplikację.

Następne kroki

W tym samouczku zostały wykonane następujące czynności:

  • Dostosowano stronę Szczegóły
  • Zaktualizowano stronę Tworzenie
  • Zaktualizowano stronę Edytuj
  • Zaktualizowano stronę Usuwanie
  • Zamknięte połączenia z bazą danych

Przejdź do następnego samouczka, aby dowiedzieć się, jak rozszerzyć funkcjonalność strony Indeks , dodając sortowanie, filtrowanie i stronicowanie.