Bagikan melalui


Tutorial: Pelajari tentang skenario tingkat lanjut - ASP.NET MVC dengan EF Core

Dalam tutorial sebelumnya, Anda menerapkan pewarisan tabel-per-hierarki. Tutorial ini memperkenalkan beberapa topik yang berguna untuk diperhatikan ketika Anda melampaui dasar-dasar pengembangan aplikasi web ASP.NET Core yang menggunakan Entity Framework Core.

Dalam tutorial ini, Anda:

  • Melakukan kueri SQL mentah
  • Panggil kueri untuk mengembalikan entitas
  • Panggil kueri untuk mengembalikan jenis lain.
  • Melakukan pemanggilan kueri pembaruan
  • Memeriksa kueri SQL
  • Membuat lapisan abstraksi
  • Pelajari tentang Deteksi perubahan otomatis
  • Pelajari tentang kode sumber EF Core dan rencana pengembangan
  • Pelajari cara menggunakan LINQ dinamis untuk menyederhanakan kode

Prerequisites

Melakukan kueri SQL mentah

Salah satu keuntungan menggunakan Kerangka Kerja Entitas adalah menghindari pengikatan kode Anda terlalu dekat dengan metode penyimpanan data tertentu. Ini dilakukan dengan menghasilkan kueri dan perintah SQL untuk Anda, yang juga membebaskan Anda dari harus menulisnya sendiri. Tetapi ada skenario luar biasa ketika Anda perlu menjalankan kueri SQL tertentu yang telah Anda buat secara manual. Untuk skenario ini, API Pertama Kode Kerangka Kerja Entitas menyertakan metode yang memungkinkan Anda meneruskan perintah SQL langsung ke database. Anda memiliki opsi berikut di EF Core 1.0:

  • Gunakan metode DbSet.FromSql untuk kueri yang mengembalikan jenis entitas. Objek yang dikembalikan harus dari jenis yang diharapkan oleh objek DbSet, dan secara otomatis dilacak oleh konteks database kecuali Anda menonaktifkan pelacakan.

  • Gunakan Database.ExecuteSqlCommand untuk perintah bukan kueri.

Jika Anda perlu menjalankan kueri yang mengembalikan tipe yang bukan entitas, Anda bisa menggunakan ADO.NET dengan koneksi database yang disediakan oleh EF. Data yang dikembalikan tidak dilacak oleh konteks database, bahkan jika Anda menggunakan metode ini untuk mengambil jenis entitas.

Seperti yang selalu benar ketika Anda menjalankan perintah SQL dalam aplikasi web, Anda harus mengambil tindakan pencegahan untuk melindungi situs Anda dari serangan injeksi SQL. Salah satu cara untuk melakukannya adalah dengan menggunakan kueri berparameter untuk memastikan bahwa string yang dikirimkan oleh halaman web tidak dapat ditafsirkan sebagai perintah SQL. Dalam tutorial ini Anda akan menggunakan kueri berparameter saat mengintegrasikan input pengguna ke dalam kueri.

Panggil kueri untuk mengembalikan entitas

Kelas DbSet<TEntity> menyediakan metode yang dapat Anda gunakan untuk menjalankan kueri yang mengembalikan entitas jenis TEntity. Untuk melihat cara kerjanya, Anda akan mengubah kode dalam metode Details dari pengendali departemen.

Dalam DepartmentsController.cs, dalam metode Details, ganti kode yang mengambil departemen dengan panggilan metode FromSql, seperti yang ditunjukkan dalam kode yang disorot berikut:

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

    string query = "SELECT * FROM Department WHERE DepartmentID = {0}";
    var department = await _context.Departments
        .FromSql(query, id)
        .Include(d => d.Administrator)
        .AsNoTracking()
        .FirstOrDefaultAsync();

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

    return View(department);
}

Untuk memverifikasi bahwa kode baru berfungsi dengan benar, pilih tab Departemen lalu Detail untuk salah satu departemen.

Department Details

Panggil kueri untuk mengembalikan jenis lain.

Sebelumnya Anda membuat kisi statistik siswa untuk halaman Tentang yang menunjukkan jumlah siswa untuk setiap tanggal pendaftaran. Anda mendapatkan data dari kumpulan entitas Siswa (_context.Students) dan menggunakan LINQ untuk memproyeksikan hasilnya ke dalam daftar objek model tampilan EnrollmentDateGroup. Misalkan Anda ingin menulis SQL itu sendiri daripada menggunakan LINQ. Untuk melakukannya, Anda perlu menjalankan kueri SQL yang mengembalikan sesuatu selain objek entitas. Dalam EF Core 1.0, salah satu cara untuk melakukannya adalah dengan menulis kode ADO.NET dan mendapatkan koneksi database dari EF.

Di HomeController.cs, ganti metode About dengan kode berikut:

public async Task<ActionResult> About()
{
    List<EnrollmentDateGroup> groups = new List<EnrollmentDateGroup>();
    var conn = _context.Database.GetDbConnection();
    try
    {
        await conn.OpenAsync();
        using (var command = conn.CreateCommand())
        {
            string query = "SELECT EnrollmentDate, COUNT(*) AS StudentCount "
                + "FROM Person "
                + "WHERE Discriminator = 'Student' "
                + "GROUP BY EnrollmentDate";
            command.CommandText = query;
            DbDataReader reader = await command.ExecuteReaderAsync();

            if (reader.HasRows)
            {
                while (await reader.ReadAsync())
                {
                    var row = new EnrollmentDateGroup { EnrollmentDate = reader.GetDateTime(0), StudentCount = reader.GetInt32(1) };
                    groups.Add(row);
                }
            }
            reader.Dispose();
        }
    }
    finally
    {
        conn.Close();
    }
    return View(groups);
}

Tambahkan pernyataan penggunaan:

using System.Data.Common;

Jalankan aplikasi dan buka halaman Tentang. Ini menampilkan data yang sama seperti sebelumnya.

About page

Melakukan pemanggilan kueri pembaruan

Misalkan administrator Contoso University ingin melakukan perubahan global dalam database, seperti mengubah jumlah kredit untuk setiap kursus. Jika universitas memiliki sejumlah besar kursus, tidak efisien untuk mengambil semuanya sebagai entitas dan mengubahnya satu per satu. Di bagian ini Anda akan menerapkan halaman web yang memungkinkan pengguna menentukan faktor untuk mengubah jumlah kredit untuk semua kursus, dan Anda akan membuat perubahan dengan menjalankan pernyataan PEMBARUAN SQL. Halaman web akan terlihat seperti ilustrasi berikut:

Update Course Credits pageUpdate Course Credits pagePerbarui halaman Kredit Kursus

Di CoursesController.cs, tambahkan metode UpdateCourseCredits untuk HttpGet dan HttpPost:

public IActionResult UpdateCourseCredits()
{
    return View();
}
[HttpPost]
public async Task<IActionResult> UpdateCourseCredits(int? multiplier)
{
    if (multiplier != null)
    {
        ViewData["RowsAffected"] = 
            await _context.Database.ExecuteSqlCommandAsync(
                "UPDATE Course SET Credits = Credits * {0}",
                parameters: multiplier);
    }
    return View();
}

Ketika pengontrol memproses permintaan HttpGet, tidak ada yang dikembalikan dalam ViewData["RowsAffected"], dan tampilan menampilkan kotak teks kosong dan tombol kirim, seperti yang ditunjukkan pada ilustrasi sebelumnya.

Ketika tombol Perbarui diklik, metode HttpPost dipanggil, dan perkalian memiliki nilai yang dimasukkan dalam kotak teks. Kode kemudian menjalankan SQL yang memperbarui kursus dan mengembalikan jumlah baris yang terpengaruh ke tampilan di ViewData. Saat tampilan mendapatkan nilai RowsAffected, tampilan akan menampilkan jumlah baris yang diperbarui.

Di Penjelajah Solusi, klik kanan folder Views/Courses, lalu klik Tambahkan Item Baru >.

Dalam dialog Tambahkan Item Baru, klik ASP.NET Core di bawah terinstal di panel kiri, klik Razor Tampilkan, dan beri nama tampilan baru UpdateCourseCredits.cshtml.

Di Views/Courses/UpdateCourseCredits.cshtml, ganti kode templat dengan kode berikut:

@{
    ViewBag.Title = "UpdateCourseCredits";
}

<h2>Update Course Credits</h2>

@if (ViewData["RowsAffected"] == null)
{
    <form asp-action="UpdateCourseCredits">
        <div class="form-actions no-color">
            <p>
                Enter a number to multiply every course's credits by: @Html.TextBox("multiplier")
            </p>
            <p>
                <input type="submit" value="Update" class="btn btn-default" />
            </p>
        </div>
    </form>
}
@if (ViewData["RowsAffected"] != null)
{
    <p>
        Number of rows updated: @ViewData["RowsAffected"]
    </p>
}
<div>
    @Html.ActionLink("Back to List", "Index")
</div>

Jalankan metode UpdateCourseCredits dengan memilih tab Kursus, lalu tambahkan "/UpdateCourseCredits" ke akhir URL di bilah alamat browser (misalnya: http://localhost:5813/Courses/UpdateCourseCredits). Masukkan angka dalam kotak teks:

Update Course Credits pageUpdate Course Credits pagePerbarui halaman Kredit Kursus

Click Update. Anda melihat jumlah baris yang terpengaruh:

Memperbarui baris halaman Kredit Kursus yang terpengaruh

Klik Kembali ke Daftar untuk melihat daftar kursus dengan jumlah kredit yang direvisi.

Perhatikan bahwa kode produksi akan memastikan bahwa pembaruan selalu menghasilkan data yang valid. Kode yang disederhanakan yang ditunjukkan di sini dapat mengalikan jumlah kredit yang cukup untuk menghasilkan angka yang lebih besar dari 5. (Properti Credits memiliki atribut [Range(0, 5)].) Kueri pembaruan akan berfungsi tetapi data yang tidak valid dapat menyebabkan hasil yang tidak terduga di bagian lain dari sistem yang mengasumsikan jumlah kredit adalah 5 atau kurang.

Untuk informasi selengkapnya tentang kueri SQL mentah, lihat Kueri SQL Mentah.

Memeriksa kueri SQL

Terkadang sangat membantu untuk dapat melihat kueri SQL aktual yang dikirim ke database. Fungsionalitas pengelogan bawaan untuk ASP.NET Core secara otomatis digunakan oleh EF Core untuk menulis log yang berisi SQL untuk kueri dan pembaruan. Di bagian ini Anda akan melihat beberapa contoh pengelogan SQL.

Buka StudentsController.cs dan dalam metode Details atur titik henti pada pernyataan if (student == null).

Jalankan aplikasi dalam mode debug, dan buka halaman Detail untuk siswa.

Buka jendela Output yang memperlihatkan output debug, lalu Anda melihat kueri:

Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (56ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30']
SELECT TOP(2) [s].[ID], [s].[Discriminator], [s].[FirstName], [s].[LastName], [s].[EnrollmentDate]
FROM [Person] AS [s]
WHERE ([s].[Discriminator] = N'Student') AND ([s].[ID] = @__id_0)
ORDER BY [s].[ID]
Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (122ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30']
SELECT [s.Enrollments].[EnrollmentID], [s.Enrollments].[CourseID], [s.Enrollments].[Grade], [s.Enrollments].[StudentID], [e.Course].[CourseID], [e.Course].[Credits], [e.Course].[DepartmentID], [e.Course].[Title]
FROM [Enrollment] AS [s.Enrollments]
INNER JOIN [Course] AS [e.Course] ON [s.Enrollments].[CourseID] = [e.Course].[CourseID]
INNER JOIN (
    SELECT TOP(1) [s0].[ID]
    FROM [Person] AS [s0]
    WHERE ([s0].[Discriminator] = N'Student') AND ([s0].[ID] = @__id_0)
    ORDER BY [s0].[ID]
) AS [t] ON [s.Enrollments].[StudentID] = [t].[ID]
ORDER BY [t].[ID]

Anda akan melihat sesuatu di sini yang mungkin mengejutkan Anda: SQL memilih hingga 2 baris (TOP(2)) dari tabel Orang. Metode SingleOrDefaultAsync tidak berhasil menjadi 1 baris di server. Here's why:

  • Jika kueri akan mengembalikan beberapa baris, metode tersebut mengembalikan null.
  • Untuk menentukan apakah kueri akan mengembalikan beberapa baris, EF harus memeriksa apakah kueri mengembalikan setidaknya 2.

Perhatikan bahwa Anda tidak perlu menggunakan mode debug dan berhenti di titik henti untuk mendapatkan logging output di jendela Output. Ini hanyalah cara mudah untuk menghentikan pengelogan pada titik yang ingin Anda lihat outputnya. Jika Anda tidak melakukannya, pengelogan berlanjut dan Anda harus menggulir kembali untuk menemukan bagian yang Anda minati.

Membuat lapisan abstraksi

Banyak pengembang menulis kode untuk mengimplementasikan repositori dan unit pola kerja sebagai pembungkus di sekitar kode yang berfungsi dengan Kerangka Kerja Entitas. Pola-pola ini dimaksudkan untuk membuat lapisan abstraksi antara lapisan akses data dan lapisan logika bisnis aplikasi. Menerapkan pola ini dapat membantu mengisolasi aplikasi Anda dari perubahan di penyimpanan data dan dapat memfasilitasi pengujian unit otomatis atau pengembangan berbasis pengujian (TDD). Namun, menulis kode tambahan untuk mengimplementasikan pola-pola ini tidak selalu menjadi pilihan terbaik untuk aplikasi yang menggunakan EF, karena beberapa alasan:

  • Kelas konteks EF itu sendiri mengisolasi kode Anda dari kode khusus penyimpanan data.

  • Kelas konteks EF dapat bertindak sebagai kelas unit kerja untuk pembaruan database yang Anda lakukan menggunakan EF.

  • EF mencakup fitur untuk menerapkan TDD tanpa menulis kode repositori.

Untuk informasi tentang cara menerapkan repositori dan unit pola kerja, lihat versi Entity Framework 5 dari seri tutorial ini.

Entity Framework Core mengimplementasikan penyedia database dalam memori yang dapat digunakan untuk pengujian. Untuk informasi selengkapnya, lihat Pengujian dengan InMemory.

Deteksi perubahan otomatis

Kerangka Kerja Entitas menentukan bagaimana entitas telah berubah (dan oleh karena itu pembaruan mana yang perlu dikirim ke database) dengan membandingkan nilai entitas saat ini dengan nilai asli. Nilai asli disimpan saat entitas dikueri atau dilampirkan. Beberapa metode yang menyebabkan deteksi perubahan otomatis adalah sebagai berikut:

  • DbContext.SaveChanges

  • DbContext.Entry

  • ChangeTracker.Entries

Jika Anda melacak sejumlah besar entitas dan memanggil salah satu metode ini berkali-kali dalam perulangan, Anda mungkin mendapatkan peningkatan performa yang signifikan dengan menonaktifkan deteksi perubahan otomatis untuk sementara menggunakan properti ChangeTracker.AutoDetectChangesEnabled. For example:

_context.ChangeTracker.AutoDetectChangesEnabled = false;

EF Core kode sumber dan rencana pengembangan

Sumber Entity Framework Core berada di https://github.com/dotnet/efcore. Repositori EF Core berisi build harian, pelacakan masalah, spesifikasi fitur, catatan rapat desain, dan roadmap untuk pengembangan masa depan. Anda dapat mengajukan atau menemukan bug, dan berkontribusi.

Meskipun kode sumber terbuka, Entity Framework Core sepenuhnya didukung sebagai produk Microsoft. Tim Microsoft Entity Framework menjaga kontrol atas kontribusi mana yang diterima dan menguji semua perubahan kode untuk memastikan kualitas setiap rilis.

Mereayasa balik dari database yang sudah ada

Untuk merekayasa balik model data termasuk kelas entitas dari database yang ada, gunakan perintah scaffold-dbcontext. Lihat tutorial memulai .

Menggunakan LINQ dinamis untuk menyederhanakan kode

Tutorial ketiga dalam seri ini menunjukkan cara menulis kode LINQ dengan meng-hard-code nama kolom dalam sebuah perintah switch. Dengan dua kolom untuk dipilih, ini berfungsi dengan baik, tetapi jika Anda memiliki banyak kolom, kode bisa menjadi panjang. Untuk mengatasi masalah tersebut, Anda dapat menggunakan metode EF.Property untuk menentukan nama properti sebagai string. Untuk mencoba pendekatan ini, ganti metode Index di StudentsController dengan kode berikut.

 public async Task<IActionResult> Index(
     string sortOrder,
     string currentFilter,
     string searchString,
     int? pageNumber)
 {
     ViewData["CurrentSort"] = sortOrder;
     ViewData["NameSortParm"] = 
         String.IsNullOrEmpty(sortOrder) ? "LastName_desc" : "";
     ViewData["DateSortParm"] = 
         sortOrder == "EnrollmentDate" ? "EnrollmentDate_desc" : "EnrollmentDate";

     if (searchString != null)
     {
         pageNumber = 1;
     }
     else
     {
         searchString = currentFilter;
     }

     ViewData["CurrentFilter"] = searchString;

     var students = from s in _context.Students
                    select s;
     
     if (!String.IsNullOrEmpty(searchString))
     {
         students = students.Where(s => s.LastName.Contains(searchString)
                                || s.FirstMidName.Contains(searchString));
     }

     if (string.IsNullOrEmpty(sortOrder))
     {
         sortOrder = "LastName";
     }

     bool descending = false;
     if (sortOrder.EndsWith("_desc"))
     {
         sortOrder = sortOrder.Substring(0, sortOrder.Length - 5);
         descending = true;
     }

     if (descending)
     {
         students = students.OrderByDescending(e => EF.Property<object>(e, sortOrder));
     }
     else
     {
         students = students.OrderBy(e => EF.Property<object>(e, sortOrder));
     }

     int pageSize = 3;
     return View(await PaginatedList<Student>.CreateAsync(students.AsNoTracking(), 
         pageNumber ?? 1, pageSize));
 }

Acknowledgments

Tom Dykstra dan Rick Anderson (twitter @RickAndMSFT) menulis tutorial ini. Rowan Miller, Diego Vega, dan anggota lain dari tim Entity Framework dibantu dengan ulasan kode dan membantu masalah debug yang muncul saat kami menulis kode untuk tutorial. John Parente dan Paul Goldman bekerja untuk memperbarui tutorial untuk ASP.NET Core 2.2.

Memecahkan masalah kesalahan umum

ContosoUniversity.dll digunakan oleh proses lain

Error message:

Tidak dapat membuka '... bin\Debug\netcoreapp1.0\ContosoUniversity.dll' for writing -- 'The process cannot access the file '...\bin\Debug\netcoreapp1.0\ContosoUniversity.dll' karena sedang digunakan oleh proses lain.

Solution:

Hentikan situs di IIS Express. Buka Baki Sistem Windows, temukan IIS Express dan klik kanan ikonnya, pilih situs Universitas Contoso, lalu klik Hentikan Situs.

Perancah migrasi tanpa kode dalam metode Naik dan Turun

Possible cause:

Perintah EF CLI tidak secara otomatis menutup dan menyimpan file kode. Jika Anda memiliki perubahan yang belum disimpan saat menjalankan perintah migrations add, EF tidak akan menemukan perubahan Anda.

Solution:

Jalankan perintah migrations remove, simpan perubahan kode Anda dan jalankan ulang perintah migrations add.

Kesalahan saat menjalankan pembaruan database

Dimungkinkan untuk mendapatkan kesalahan lain saat membuat perubahan skema dalam database yang memiliki data yang ada. Jika Anda mendapatkan kesalahan migrasi yang tidak dapat Anda atasi, Anda bisa mengubah nama database dalam string koneksi atau menghapus database. Dengan database baru, tidak ada data untuk dimigrasikan, dan perintah update-database jauh lebih mungkin diselesaikan tanpa kesalahan.

Pendekatan paling sederhana adalah mengganti nama database di appsettings.json. Lain kali Anda menjalankan database update, database baru akan dibuat.

Untuk menghapus database di SSOX, klik kanan database, klik Hapus, lalu dalam kotak dialog Hapus Database pilih Tutup koneksi yang ada dan klik OK.

Untuk menghapus database dengan menggunakan CLI, jalankan perintah CLI database drop:

dotnet ef database drop

Kesalahan menemukan instans SQL Server

Error Message:

Terjadi kesalahan terkait jaringan atau khusus instans saat membuat koneksi ke SQL Server. Server tidak ditemukan atau tidak dapat diakses. Verifikasi bahwa nama instans sudah benar dan SQL Server dikonfigurasi untuk mengizinkan koneksi jarak jauh. (penyedia: Antarmuka Jaringan SQL, kesalahan: 26 - Kesalahan Menemukan Server/Instans Ditentukan)

Solution:

Periksa string koneksi. Jika Anda telah menghapus file database secara manual, ubah nama database dalam string konstruksi untuk memulai kembali dengan database baru.

Dapatkan kode

Unduh atau lihat aplikasi yang telah selesai.

Additional resources

Untuk informasi selengkapnya tentang EF Core, lihat dokumentasi Entity Framework Core. Buku juga tersedia: Entity Framework Core in Action.

Untuk informasi tentang cara menyebarkan aplikasi web, lihat Host dan menyebarkan ASP.NET Core.

Untuk informasi tentang topik lain yang terkait dengan ASP.NET Core MVC, seperti autentikasi dan otorisasi, lihat Ringkasan ASP.NET Core.

Untuk panduan tentang membuat aplikasi ASP.NET Core yang andal, aman, berkinerja, dapat diuji, dan dapat diskalakan, lihat pola aplikasi web Enterprise. Sampel aplikasi web berkualitas produksi yang lengkap dan mengimplementasikan pola telah tersedia.

Next steps

Dalam tutorial ini, Anda:

  • Melakukan kueri SQL mentah
  • Memanggil kueri untuk mengembalikan entitas
  • Memanggil sebuah kueri untuk mengembalikan jenis lain
  • Disebut kueri pembaruan
  • Kueri SQL diperiksa
  • Membuat lapisan abstraksi
  • Dipelajari tentang Deteksi perubahan otomatis
  • Mempelajari tentang kode sumber EF Core dan rencana pengembangan
  • Mempelajari cara menggunakan LINQ dinamis untuk menyederhanakan kode

Ini menyelesaikan rangkaian tutorial ini tentang menggunakan Entity Framework Core dalam aplikasi MVC Core ASP.NET. Seri ini bekerja dengan database baru; alternatifnya adalah merekayasa balik model dari database yang ada.