Tutorial: Membuat model data kompleks - ASP.NET MVC dengan EF Core
Dalam tutorial sebelumnya, Anda bekerja dengan model data sederhana yang terdiri dari tiga entitas. Dalam tutorial ini, Anda akan menambahkan lebih banyak entitas dan hubungan dan Anda akan menyesuaikan model data dengan menentukan aturan pemformatan, validasi, dan pemetaan database.
Setelah selesai, kelas entitas akan membentuk model data lengkap yang ditampilkan dalam ilustrasi berikut:
Di tutorial ini, Anda akan:
- Mengkustomisasi model Data
- Membuat perubahan pada entitas Siswa
- Membuat entitas Instruktur
- Membuat entitas OfficeAssignment
- Ubah entitas Kursus
- Membuat entitas Departemen
- Mengubah entitas Pendaftaran
- Memperbarui konteks database
- Database benih dengan data pengujian
- Menambahkan migrasi
- Mengubah string koneksi
- Memperbarui database
Prasyarat
Mengkustomisasi model Data
Di bagian ini Anda akan melihat cara mengkustomisasi model data dengan menggunakan atribut yang menentukan aturan pemformatan, validasi, dan pemetaan database. Kemudian di beberapa bagian berikut, Anda akan membuat model data Sekolah lengkap dengan menambahkan atribut ke kelas yang sudah Anda buat dan buat kelas baru untuk jenis entitas yang tersisa dalam model.
Atribut DataType
Untuk tanggal pendaftaran siswa, semua halaman web saat ini menampilkan waktu bersama dengan tanggal, meskipun yang Anda pedulikan untuk bidang ini adalah tanggal. Dengan menggunakan atribut anotasi data, Anda dapat membuat satu perubahan kode yang akan memperbaiki format tampilan di setiap tampilan yang menunjukkan data. Untuk melihat contoh cara melakukannya, Anda akan menambahkan atribut ke EnrollmentDate
properti di Student
kelas .
Di Models/Student.cs
, tambahkan using
pernyataan untuk System.ComponentModel.DataAnnotations
namespace layanan dan tambahkan DataType
DisplayFormat
atribut ke EnrollmentDate
properti , seperti yang ditunjukkan dalam contoh berikut:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace ContosoUniversity.Models
{
public class Student
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime EnrollmentDate { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
}
}
Atribut DataType
digunakan untuk menentukan jenis data yang lebih spesifik daripada jenis intrinsik database. Dalam hal ini kita hanya ingin melacak tanggal, bukan tanggal dan waktu. DataType
Enumerasi menyediakan banyak jenis data, seperti Tanggal, Waktu, Telepon Number, Mata Uang, EmailAddress, dan banyak lagi. Atribut ini DataType
juga dapat memungkinkan aplikasi untuk secara otomatis menyediakan fitur khusus jenis. Misalnya, mailto:
tautan dapat dibuat untuk DataType.EmailAddress
, dan pemilih tanggal dapat disediakan untuk DataType.Date
di browser yang mendukung HTML5. Atribut memancarkan DataType
atribut HTML 5 data-
(tanda hubung data yang diucapkan) yang dapat dipahami browser HTML 5. Atribut DataType
tidak memberikan validasi apa pun.
DataType.Date
tidak menentukan format tanggal yang ditampilkan. Secara default, bidang data ditampilkan sesuai dengan format default berdasarkan CultureInfo server.
Atribut DisplayFormat
digunakan untuk secara eksplisit menentukan format tanggal:
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
Pengaturan ApplyFormatInEditMode
menentukan bahwa pemformatan juga harus diterapkan saat nilai ditampilkan dalam kotak teks untuk pengeditan. (Anda mungkin tidak menginginkannya untuk beberapa bidang -- misalnya, untuk nilai mata uang, Anda mungkin tidak ingin simbol mata uang dalam kotak teks untuk diedit.)
Anda dapat menggunakan atribut dengan DisplayFormat
sendirinya, tetapi umumnya merupakan ide yang baik untuk menggunakan DataType
atribut juga. Atribut DataType
ini menyampaikan semantik data dibandingkan dengan cara merendernya di layar, dan memberikan manfaat berikut yang tidak Anda dapatkan dengan DisplayFormat
:
Browser dapat mengaktifkan fitur HTML5 (misalnya untuk menampilkan kontrol kalender, simbol mata uang yang sesuai dengan lokal, tautan email, beberapa validasi input sisi klien, dll.).
Secara default, browser akan merender data menggunakan format yang benar berdasarkan lokal Anda.
Untuk informasi selengkapnya, lihat dokumentasi pembantu< tag input>.
Jalankan aplikasi, buka halaman Indeks Siswa dan perhatikan bahwa waktu tidak lagi ditampilkan untuk tanggal pendaftaran. Hal yang sama akan berlaku untuk tampilan apa pun yang menggunakan model Siswa.
Atribut StringLength
Anda juga dapat menentukan aturan validasi data dan pesan kesalahan validasi menggunakan atribut. Atribut StringLength
mengatur panjang maksimum dalam database dan menyediakan validasi sisi klien dan sisi server untuk ASP.NET Core MVC. Anda juga dapat menentukan panjang string minimum dalam atribut ini, tetapi nilai minimum tidak berdampak pada skema database.
Misalkan Anda ingin memastikan bahwa pengguna tidak memasukkan lebih dari 50 karakter untuk nama. Untuk menambahkan batasan ini, tambahkan StringLength
atribut ke LastName
properti dan FirstMidName
, seperti yang ditunjukkan dalam contoh berikut:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace ContosoUniversity.Models
{
public class Student
{
public int ID { get; set; }
[StringLength(50)]
public string LastName { get; set; }
[StringLength(50)]
public string FirstMidName { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime EnrollmentDate { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
}
}
Atribut StringLength
tidak akan mencegah pengguna memasukkan spasi kosong untuk nama. Anda dapat menggunakan RegularExpression
atribut untuk menerapkan pembatasan pada input. Misalnya, kode berikut mengharuskan karakter pertama menjadi huruf besar dan karakter yang tersisa menjadi alfabet:
[RegularExpression(@"^[A-Z]+[a-zA-Z]*$")]
Atribut MaxLength
ini menyediakan fungsionalitas yang StringLength
mirip dengan atribut tetapi tidak memberikan validasi sisi klien.
Model database sekarang telah berubah dengan cara yang memerlukan perubahan dalam skema database. Anda akan menggunakan migrasi untuk memperbarui skema tanpa kehilangan data apa pun yang mungkin telah Anda tambahkan ke database dengan menggunakan UI aplikasi.
Simpan perubahan Anda dan bangun proyek. Kemudian buka jendela perintah di folder proyek dan masukkan perintah berikut:
dotnet ef migrations add MaxLengthOnNames
dotnet ef database update
Perintah migrations add
memperingatkan bahwa kehilangan data dapat terjadi, karena perubahan membuat panjang maksimum lebih pendek untuk dua kolom. Migrasi membuat file bernama <timeStamp>_MaxLengthOnNames.cs
. File ini berisi kode dalam Up
metode yang akan memperbarui database agar sesuai dengan model data saat ini. Perintah database update
menjalankan kode itu.
Tanda waktu yang diawali dengan nama file migrasi digunakan oleh Kerangka Kerja Entitas untuk memesan migrasi. Anda dapat membuat beberapa migrasi sebelum menjalankan perintah update-database, lalu semua migrasi diterapkan dalam urutan pembuatannya.
Jalankan aplikasi, pilih tab Siswa , klik Buat Baru, dan coba masukkan nama yang lebih panjang dari 50 karakter. Aplikasi harus mencegah Anda melakukan ini.
Atribut Kolom
Anda juga dapat menggunakan atribut untuk mengontrol bagaimana kelas dan properti Anda dipetakan ke database. Misalkan Anda telah menggunakan nama FirstMidName
untuk bidang nama depan karena bidang mungkin juga berisi nama tengah. Tetapi Anda ingin kolom database diberi nama FirstName
, karena pengguna yang akan menulis kueri ad-hoc terhadap database terbiasa dengan nama tersebut. Untuk membuat pemetaan ini, Anda dapat menggunakan Column
atribut .
Atribut Column
menentukan bahwa ketika database dibuat, kolom Student
tabel yang memetakan ke FirstMidName
properti akan diberi nama FirstName
. Dengan kata lain, ketika kode Anda merujuk ke Student.FirstMidName
, data akan berasal dari atau diperbarui di FirstName
kolom Student
tabel. Jika Anda tidak menentukan nama kolom, nama kolom tersebut diberi nama yang sama dengan nama properti.
Student.cs
Dalam file, tambahkan pernyataan untuk System.ComponentModel.DataAnnotations.Schema
dan tambahkan atribut nama kolom ke FirstMidName
properti , seperti yang using
ditunjukkan dalam kode yang disorot berikut:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public class Student
{
public int ID { get; set; }
[StringLength(50)]
public string LastName { get; set; }
[StringLength(50)]
[Column("FirstName")]
public string FirstMidName { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime EnrollmentDate { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
}
}
Penambahan Column
atribut mengubah model yang SchoolContext
mendukung , sehingga tidak akan cocok dengan database.
Simpan perubahan Anda dan bangun proyek. Kemudian buka jendela perintah di folder proyek dan masukkan perintah berikut untuk membuat migrasi lain:
dotnet ef migrations add ColumnFirstName
dotnet ef database update
Di SQL Server Object Explorer, buka perancang tabel Siswa dengan mengklik ganda tabel Siswa .
Sebelum Anda menerapkan dua migrasi pertama, kolom nama berjenis nvarchar(MAX). Sekarang adalah nvarchar(50) dan nama kolom telah berubah dari FirstMidName menjadi FirstName.
Catatan
Jika Anda mencoba mengkompilasi sebelum selesai membuat semua kelas entitas di bagian berikut, Anda mungkin mendapatkan kesalahan kompilator.
Perubahan pada entitas Siswa
Di Models/Student.cs
, ganti kode yang Anda tambahkan sebelumnya dengan kode berikut. Perubahan disorot.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public class Student
{
public int ID { get; set; }
[Required]
[StringLength(50)]
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Required]
[StringLength(50)]
[Column("FirstName")]
[Display(Name = "First Name")]
public string FirstMidName { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
[Display(Name = "Enrollment Date")]
public DateTime EnrollmentDate { get; set; }
[Display(Name = "Full Name")]
public string FullName
{
get
{
return LastName + ", " + FirstMidName;
}
}
public ICollection<Enrollment> Enrollments { get; set; }
}
}
Atribut Wajib
Atribut Required
membuat properti nama menjadi bidang yang diperlukan. Atribut Required
tidak diperlukan untuk jenis yang tidak dapat diubah ke null seperti jenis nilai (DateTime, int, double, float, dll.). Jenis yang tidak boleh null secara otomatis diperlakukan sebagai bidang yang diperlukan.
Atribut Required
harus digunakan untuk MinimumLength
MinimumLength
diberlakukan.
[Display(Name = "Last Name")]
[Required]
[StringLength(50, MinimumLength=2)]
public string LastName { get; set; }
Atribut Tampilan
Atribut Display
menentukan bahwa keterangan untuk kotak teks harus "Nama Depan", "Nama Belakang", "Nama Lengkap", dan "Tanggal Pendaftaran" alih-alih nama properti di setiap instans (yang tidak memiliki ruang membagi kata).
Properti terhitung FullName
FullName
adalah properti terhitung yang mengembalikan nilai yang dibuat dengan menggabungkan dua properti lainnya. Oleh karena itu hanya memiliki aksesor get, dan tidak ada FullName
kolom yang akan dihasilkan dalam database.
Membuat entitas Instruktur
Buat Models/Instructor.cs
, mengganti kode templat dengan kode berikut:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public class Instructor
{
public int ID { get; set; }
[Required]
[Display(Name = "Last Name")]
[StringLength(50)]
public string LastName { get; set; }
[Required]
[Column("FirstName")]
[Display(Name = "First Name")]
[StringLength(50)]
public string FirstMidName { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
[Display(Name = "Hire Date")]
public DateTime HireDate { get; set; }
[Display(Name = "Full Name")]
public string FullName
{
get { return LastName + ", " + FirstMidName; }
}
public ICollection<CourseAssignment> CourseAssignments { get; set; }
public OfficeAssignment OfficeAssignment { get; set; }
}
}
Perhatikan bahwa beberapa properti sama di entitas Siswa dan Instruktur. Dalam tutorial Implementing Inheritance nanti dalam seri ini, Anda akan merefaktor kode ini untuk menghilangkan redundansi.
Anda dapat menempatkan beberapa atribut pada satu baris, sehingga Anda juga dapat menulis HireDate
atribut sebagai berikut:
[DataType(DataType.Date),Display(Name = "Hire Date"),DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
Properti navigasi CourseAssignments dan OfficeAssignment
Properti CourseAssignments
dan OfficeAssignment
adalah properti navigasi.
Instruktur dapat mengajarkan sejumlah kursus, sehingga CourseAssignments
didefinisikan sebagai koleksi.
public ICollection<CourseAssignment> CourseAssignments { get; set; }
Jika properti navigasi dapat menampung beberapa entitas, jenisnya harus berupa daftar di mana entri dapat ditambahkan, dihapus, dan diperbarui. Anda dapat menentukan ICollection<T>
atau jenis seperti List<T>
atau HashSet<T>
. Jika Anda menentukan ICollection<T>
, EF membuat HashSet<T>
koleksi secara default.
Alasan mengapa ini adalah CourseAssignment
entitas dijelaskan di bawah ini di bagian tentang hubungan banyak ke banyak.
Aturan bisnis Contoso University menyatakan bahwa instruktur hanya dapat memiliki paling banyak satu kantor, sehingga OfficeAssignment
properti memiliki satu entitas OfficeAssignment (yang mungkin null jika tidak ada kantor yang ditetapkan).
public OfficeAssignment OfficeAssignment { get; set; }
Membuat entitas OfficeAssignment
Buat Models/OfficeAssignment.cs
dengan kode berikut:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public class OfficeAssignment
{
[Key]
public int InstructorID { get; set; }
[StringLength(50)]
[Display(Name = "Office Location")]
public string Location { get; set; }
public Instructor Instructor { get; set; }
}
}
Atribut Kunci
Ada hubungan satu-ke-nol-atau-satu antara Instructor
dan OfficeAssignment
entitas. Penugasan kantor hanya ada dalam kaitannya dengan instruktur yang ditetapkannya, dan oleh karena itu kunci utamanya juga merupakan kunci asingnya untuk Instructor
entitas. Tetapi Kerangka Kerja Entitas tidak dapat secara otomatis dikenali InstructorID
sebagai kunci utama entitas ini karena namanya tidak mengikuti ID
konvensi penamaan atau classnameID
. Oleh karena itu, Key
atribut digunakan untuk mengidentifikasinya sebagai kunci:
[Key]
public int InstructorID { get; set; }
Anda juga dapat menggunakan Key
atribut jika entitas memang memiliki kunci utamanya sendiri tetapi Anda ingin memberi nama properti sesuatu selain classnameID atau ID.
Secara default, EF memperlakukan kunci sebagai non-database yang dihasilkan karena kolom adalah untuk hubungan identifikasi.
Properti navigasi Instruktur
Entitas Instruktur memiliki properti navigasi yang dapat diubah ke OfficeAssignment
null (karena instruktur mungkin tidak memiliki penetapan kantor), dan entitas OfficeAssignment memiliki properti navigasi yang tidak dapat Instructor
diubah ke null (karena penetapan kantor tidak dapat ada tanpa instruktur -- InstructorID
tidak dapat diubah ke null). Ketika entitas Instruktur memiliki entitas OfficeAssignment terkait, setiap entitas akan memiliki referensi ke entitas lainnya di properti navigasinya.
Anda dapat meletakkan [Required]
atribut pada properti navigasi Instruktur untuk menentukan bahwa harus ada instruktur terkait, tetapi Anda tidak perlu melakukannya karena InstructorID
kunci asing (yang juga merupakan kunci untuk tabel ini) tidak dapat diubah ke null.
Ubah entitas Kursus
Di Models/Course.cs
, ganti kode yang Anda tambahkan sebelumnya dengan kode berikut. Perubahan disorot.
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public class Course
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Display(Name = "Number")]
public int CourseID { get; set; }
[StringLength(50, MinimumLength = 3)]
public string Title { get; set; }
[Range(0, 5)]
public int Credits { get; set; }
public int DepartmentID { get; set; }
public Department Department { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
public ICollection<CourseAssignment> CourseAssignments { get; set; }
}
}
Entitas kursus memiliki properti DepartmentID
kunci asing yang menunjuk ke entitas Departemen terkait dan memiliki Department
properti navigasi.
Kerangka Kerja Entitas tidak mengharuskan Anda menambahkan properti kunci asing ke model data Anda saat Anda memiliki properti navigasi untuk entitas terkait. EF secara otomatis membuat kunci asing dalam database di mana pun mereka diperlukan dan membuat properti bayangan untuk mereka. Tetapi memiliki kunci asing dalam model data dapat membuat pembaruan lebih sederhana dan lebih efisien. Misalnya, saat Anda mengambil Course
entitas untuk diedit, Department
entitas null jika Anda tidak memuatnya, jadi ketika Anda memperbarui Course
entitas, Anda harus terlebih dahulu mengambil Department
entitas. Saat properti DepartmentID
kunci asing disertakan dalam model data, Anda tidak perlu mengambil Department
entitas sebelum memperbarui.
Atribut DatabaseGenerated
Atribut DatabaseGenerated
dengan None
parameter pada CourseID
properti menentukan bahwa nilai kunci utama disediakan oleh pengguna daripada yang dihasilkan oleh database.
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Display(Name = "Number")]
public int CourseID { get; set; }
Secara default, Entity Framework mengasumsikan bahwa nilai kunci utama dihasilkan oleh database. Itulah yang Anda inginkan dalam sebagian besar skenario. Namun, untuk Course
entitas, Anda akan menggunakan nomor kursus yang ditentukan pengguna seperti seri 1000 untuk satu departemen, seri 2000 untuk departemen lain, dan sebagainya.
Atribut DatabaseGenerated
juga dapat digunakan untuk menghasilkan nilai default, seperti dalam kasus kolom database yang digunakan untuk merekam tanggal baris dibuat atau diperbarui. Untuk informasi selengkapnya, lihat Properti yang Dihasilkan.
Properti kunci dan navigasi asing
Properti kunci asing dan properti navigasi di Course
entitas mencerminkan hubungan berikut:
Kursus ditugaskan ke satu departemen, jadi ada DepartmentID
kunci asing dan Department
properti navigasi karena alasan yang disebutkan di atas.
public int DepartmentID { get; set; }
public Department Department { get; set; }
Kursus dapat memiliki sejumlah siswa yang terdaftar di dalamnya, sehingga Enrollments
properti navigasi adalah koleksi:
public ICollection<Enrollment> Enrollments { get; set; }
Kursus dapat diajarkan oleh beberapa instruktur, sehingga CourseAssignments
properti navigasi adalah koleksi (jenisnya CourseAssignment
dijelaskan nanti):
public ICollection<CourseAssignment> CourseAssignments { get; set; }
Membuat entitas Departemen
Buat Models/Department.cs
dengan kode berikut:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public class Department
{
public int DepartmentID { get; set; }
[StringLength(50, MinimumLength = 3)]
public string Name { get; set; }
[DataType(DataType.Currency)]
[Column(TypeName = "money")]
public decimal Budget { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
[Display(Name = "Start Date")]
public DateTime StartDate { get; set; }
public int? InstructorID { get; set; }
public Instructor Administrator { get; set; }
public ICollection<Course> Courses { get; set; }
}
}
Atribut Kolom
Sebelumnya Anda menggunakan Column
atribut untuk mengubah pemetaan nama kolom. Dalam kode untuk Department
entitas, Column
atribut sedang digunakan untuk mengubah pemetaan jenis data SQL sehingga kolom akan ditentukan menggunakan jenis SQL Server money
dalam database:
[Column(TypeName="money")]
public decimal Budget { get; set; }
Pemetaan kolom umumnya tidak diperlukan, karena Kerangka Kerja Entitas memilih jenis data SQL Server yang sesuai berdasarkan jenis CLR yang Anda tentukan untuk properti . Jenis CLR decimal
memetakan ke jenis SQL Server decimal
. Tetapi dalam hal ini Anda tahu bahwa kolom akan menyimpan jumlah mata uang, dan jenis data uang lebih sesuai untuk itu.
Properti kunci dan navigasi asing
Properti kunci asing dan navigasi mencerminkan hubungan berikut:
Departemen mungkin atau mungkin tidak memiliki administrator, dan administrator selalu menjadi instruktur. InstructorID
Oleh karena itu properti disertakan sebagai kunci asing ke entitas Instruktur, dan tanda tanya ditambahkan setelah int
penunjukan jenis untuk menandai properti sebagai nullable. Properti navigasi diberi nama Administrator
tetapi menyimpan entitas Instruktur:
public int? InstructorID { get; set; }
public Instructor Administrator { get; set; }
Departemen mungkin memiliki banyak kursus, jadi ada properti navigasi Kursus:
public ICollection<Course> Courses { get; set; }
Catatan
Menurut konvensi, Kerangka Kerja Entitas memungkinkan penghapusan berjenjang untuk kunci asing yang tidak dapat diubah ke null dan untuk hubungan banyak ke banyak. Ini dapat mengakibatkan aturan penghapusan kaskade melingkar, yang akan menyebabkan pengecualian ketika Anda mencoba menambahkan migrasi. Misalnya, jika Anda tidak menentukan Department.InstructorID
properti sebagai nullable, EF akan mengonfigurasi aturan penghapusan berjenjang untuk menghapus departemen saat Anda menghapus instruktur, yang bukan yang ingin Anda lakukan. Jika aturan bisnis Anda mengharuskan InstructorID
properti tidak dapat diubah ke null, Anda harus menggunakan pernyataan API fasih berikut untuk menonaktifkan penghapusan bertingkat pada hubungan:
modelBuilder.Entity<Department>()
.HasOne(d => d.Administrator)
.WithMany()
.OnDelete(DeleteBehavior.Restrict)
Mengubah entitas Pendaftaran
Di Models/Enrollment.cs
, ganti kode yang Anda tambahkan sebelumnya dengan kode berikut:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public enum Grade
{
A, B, C, D, F
}
public class Enrollment
{
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
[DisplayFormat(NullDisplayText = "No grade")]
public Grade? Grade { get; set; }
public Course Course { get; set; }
public Student Student { get; set; }
}
}
Properti kunci dan navigasi asing
Properti kunci asing dan properti navigasi mencerminkan hubungan berikut:
Catatan pendaftaran adalah untuk satu kursus, jadi ada CourseID
properti kunci asing dan Course
properti navigasi:
public int CourseID { get; set; }
public Course Course { get; set; }
Catatan pendaftaran adalah untuk satu siswa, jadi ada StudentID
properti kunci asing dan Student
properti navigasi:
public int StudentID { get; set; }
public Student Student { get; set; }
Hubungan banyak-ke-Banyak
Ada hubungan banyak ke banyak antara Student
entitas dan Course
, dan Enrollment
entitas berfungsi sebagai tabel gabungan banyak-ke-banyak dengan payload dalam database. "Dengan payload" berarti bahwa Enrollment
tabel berisi data tambahan selain kunci asing untuk tabel yang digabungkan (dalam hal ini, kunci primer dan Grade
properti).
Ilustrasi berikut menunjukkan seperti apa hubungan ini dalam diagram entitas. (Diagram ini dihasilkan menggunakan Entity Framework Power Tools untuk EF 6.x; membuat diagram bukan bagian dari tutorial, itu hanya digunakan di sini sebagai ilustrasi.)
Setiap garis hubungan memiliki 1 di satu ujung dan tanda bintang (*) di ujung lainnya, menunjukkan hubungan satu-ke-banyak.
Enrollment
Jika tabel tidak menyertakan informasi nilai, tabel hanya perlu berisi dua kunci CourseID
asing dan StudentID
. Dalam hal ini, itu akan menjadi tabel gabungan banyak ke banyak tanpa payload (atau tabel gabungan murni) dalam database. Entitas Instructor
dan Course
memiliki hubungan banyak ke banyak, dan langkah Anda selanjutnya adalah membuat kelas entitas untuk berfungsi sebagai tabel gabungan tanpa payload.
EF Core mendukung tabel gabungan implisit untuk hubungan banyak ke banyak, tetapi tutoral ini belum diperbarui untuk menggunakan tabel gabungan implisit. Lihat Hubungan Banyak ke Banyak, Razor versi Halaman dari tutorial ini yang telah diperbarui.
Entitas CourseAssignment
Buat Models/CourseAssignment.cs
dengan kode berikut:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public class CourseAssignment
{
public int InstructorID { get; set; }
public int CourseID { get; set; }
public Instructor Instructor { get; set; }
public Course Course { get; set; }
}
}
Menggabungkan nama entitas
Tabel gabungan diperlukan dalam database untuk hubungan banyak ke banyak Kursus Instruktur, dan harus diwakili oleh kumpulan entitas. Adalah umum untuk memberi nama entitas EntityName1EntityName2
gabungan , yang dalam hal ini adalah CourseInstructor
. Namun, kami sarankan Anda memilih nama yang menjelaskan hubungan. Model data dimulai dengan sederhana dan berkembang, dengan gabungan tanpa payload sering mendapatkan payload nanti. Jika Anda memulai dengan nama entitas deskriptif, Anda tidak perlu mengubah nama nanti. Idealnya, entitas gabungan akan memiliki nama alami sendiri (mungkin kata tunggal) di domain bisnis. Misalnya, Buku dan Pelanggan dapat ditautkan melalui Peringkat. Untuk hubungan ini, CourseAssignment
adalah pilihan yang lebih baik daripada CourseInstructor
.
Kunci komposit
Karena kunci asing tidak dapat diubah ke null dan bersama-sama secara unik mengidentifikasi setiap baris tabel, tidak perlu kunci primer terpisah. Properti InstructorID
dan CourseID
harus berfungsi sebagai kunci primer komposit. Satu-satunya cara untuk mengidentifikasi kunci primer komposit ke EF adalah dengan menggunakan API yang fasih (tidak dapat dilakukan dengan menggunakan atribut). Anda akan melihat cara mengonfigurasi kunci primer komposit di bagian berikutnya.
Kunci komposit memastikan bahwa meskipun Anda dapat memiliki beberapa baris untuk satu kursus, dan beberapa baris untuk satu instruktur, Anda tidak dapat memiliki beberapa baris untuk instruktur dan kursus yang sama. Entitas Enrollment
gabungan mendefinisikan kunci utamanya sendiri, sehingga duplikat dari jenis ini dimungkinkan. Untuk mencegah duplikat tersebut, Anda dapat menambahkan indeks unik pada bidang kunci asing, atau mengonfigurasi Enrollment
dengan kunci komposit utama yang mirip CourseAssignment
dengan . Untuk informasi selengkapnya, lihat Indeks.
Memperbarui konteks database
Tambahkan kode yang disorot berikut ke Data/SchoolContext.cs
file:
using ContosoUniversity.Models;
using Microsoft.EntityFrameworkCore;
namespace ContosoUniversity.Data
{
public class SchoolContext : DbContext
{
public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
{
}
public DbSet<Course> Courses { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Student> Students { get; set; }
public DbSet<Department> Departments { get; set; }
public DbSet<Instructor> Instructors { get; set; }
public DbSet<OfficeAssignment> OfficeAssignments { get; set; }
public DbSet<CourseAssignment> CourseAssignments { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Course>().ToTable("Course");
modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
modelBuilder.Entity<Student>().ToTable("Student");
modelBuilder.Entity<Department>().ToTable("Department");
modelBuilder.Entity<Instructor>().ToTable("Instructor");
modelBuilder.Entity<OfficeAssignment>().ToTable("OfficeAssignment");
modelBuilder.Entity<CourseAssignment>().ToTable("CourseAssignment");
modelBuilder.Entity<CourseAssignment>()
.HasKey(c => new { c.CourseID, c.InstructorID });
}
}
}
Kode ini menambahkan entitas baru dan mengonfigurasi kunci primer komposit entitas CourseAssignment.
Tentang alternatif API yang fasih
Kode dalam OnModelCreating
metode DbContext
kelas menggunakan API fasih untuk mengonfigurasi perilaku EF. API disebut "fasih" karena sering digunakan dengan merangkai serangkaian panggilan metode bersama-sama ke dalam satu pernyataan, seperti dalam contoh ini dari EF Core dokumentasi:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Url)
.IsRequired();
}
Dalam tutorial ini, Anda menggunakan API fasih hanya untuk pemetaan database yang tidak dapat Anda lakukan dengan atribut. Namun, Anda juga dapat menggunakan API fasih untuk menentukan sebagian besar aturan pemformatan, validasi, dan pemetaan yang dapat Anda lakukan dengan menggunakan atribut. Beberapa atribut seperti MinimumLength
tidak dapat diterapkan dengan API yang fasih. Seperti disebutkan sebelumnya, MinimumLength
tidak mengubah skema, itu hanya menerapkan aturan validasi sisi klien dan server.
Beberapa pengembang lebih suka menggunakan API yang fasih secara eksklusif sehingga mereka dapat menjaga kelas entitas mereka tetap "bersih." Anda dapat mencampur atribut dan API yang fasih jika mau, dan ada beberapa penyesuaian yang hanya dapat dilakukan dengan menggunakan API yang fasih, tetapi secara umum praktik yang direkomendasikan adalah memilih salah satu dari dua pendekatan ini dan menggunakannya secara konsisten sebanyak mungkin. Jika Anda menggunakan keduanya, perhatikan bahwa di mana pun ada konflik, Fluent API mengambil alih atribut.
Untuk informasi selengkapnya tentang atribut vs. API yang fasih, lihat Metode konfigurasi.
Diagram Entitas Memperlihatkan Hubungan
Ilustrasi berikut menunjukkan diagram yang dibuat Entity Framework Power Tools untuk model Sekolah yang telah selesai.
Selain garis hubungan satu ke banyak (1 hingga *), Anda dapat melihat di sini garis hubungan satu-ke-nol-atau-satu (1 hingga 0,.1) antara Instructor
entitas dan OfficeAssignment
dan garis hubungan nol atau satu-ke-banyak (0,.1 ke *) antara entitas Instruktur dan Departemen.
Database benih dengan data pengujian
Ganti kode dalam Data/DbInitializer.cs
file dengan kode berikut untuk menyediakan data benih untuk entitas baru yang telah Anda buat.
using System;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using ContosoUniversity.Models;
namespace ContosoUniversity.Data
{
public static class DbInitializer
{
public static void Initialize(SchoolContext context)
{
//context.Database.EnsureCreated();
// Look for any students.
if (context.Students.Any())
{
return; // DB has been seeded
}
var students = new Student[]
{
new Student { FirstMidName = "Carson", LastName = "Alexander",
EnrollmentDate = DateTime.Parse("2010-09-01") },
new Student { FirstMidName = "Meredith", LastName = "Alonso",
EnrollmentDate = DateTime.Parse("2012-09-01") },
new Student { FirstMidName = "Arturo", LastName = "Anand",
EnrollmentDate = DateTime.Parse("2013-09-01") },
new Student { FirstMidName = "Gytis", LastName = "Barzdukas",
EnrollmentDate = DateTime.Parse("2012-09-01") },
new Student { FirstMidName = "Yan", LastName = "Li",
EnrollmentDate = DateTime.Parse("2012-09-01") },
new Student { FirstMidName = "Peggy", LastName = "Justice",
EnrollmentDate = DateTime.Parse("2011-09-01") },
new Student { FirstMidName = "Laura", LastName = "Norman",
EnrollmentDate = DateTime.Parse("2013-09-01") },
new Student { FirstMidName = "Nino", LastName = "Olivetto",
EnrollmentDate = DateTime.Parse("2005-09-01") }
};
foreach (Student s in students)
{
context.Students.Add(s);
}
context.SaveChanges();
var instructors = new Instructor[]
{
new Instructor { FirstMidName = "Kim", LastName = "Abercrombie",
HireDate = DateTime.Parse("1995-03-11") },
new Instructor { FirstMidName = "Fadi", LastName = "Fakhouri",
HireDate = DateTime.Parse("2002-07-06") },
new Instructor { FirstMidName = "Roger", LastName = "Harui",
HireDate = DateTime.Parse("1998-07-01") },
new Instructor { FirstMidName = "Candace", LastName = "Kapoor",
HireDate = DateTime.Parse("2001-01-15") },
new Instructor { FirstMidName = "Roger", LastName = "Zheng",
HireDate = DateTime.Parse("2004-02-12") }
};
foreach (Instructor i in instructors)
{
context.Instructors.Add(i);
}
context.SaveChanges();
var departments = new Department[]
{
new Department { Name = "English", Budget = 350000,
StartDate = DateTime.Parse("2007-09-01"),
InstructorID = instructors.Single( i => i.LastName == "Abercrombie").ID },
new Department { Name = "Mathematics", Budget = 100000,
StartDate = DateTime.Parse("2007-09-01"),
InstructorID = instructors.Single( i => i.LastName == "Fakhouri").ID },
new Department { Name = "Engineering", Budget = 350000,
StartDate = DateTime.Parse("2007-09-01"),
InstructorID = instructors.Single( i => i.LastName == "Harui").ID },
new Department { Name = "Economics", Budget = 100000,
StartDate = DateTime.Parse("2007-09-01"),
InstructorID = instructors.Single( i => i.LastName == "Kapoor").ID }
};
foreach (Department d in departments)
{
context.Departments.Add(d);
}
context.SaveChanges();
var courses = new Course[]
{
new Course {CourseID = 1050, Title = "Chemistry", Credits = 3,
DepartmentID = departments.Single( s => s.Name == "Engineering").DepartmentID
},
new Course {CourseID = 4022, Title = "Microeconomics", Credits = 3,
DepartmentID = departments.Single( s => s.Name == "Economics").DepartmentID
},
new Course {CourseID = 4041, Title = "Macroeconomics", Credits = 3,
DepartmentID = departments.Single( s => s.Name == "Economics").DepartmentID
},
new Course {CourseID = 1045, Title = "Calculus", Credits = 4,
DepartmentID = departments.Single( s => s.Name == "Mathematics").DepartmentID
},
new Course {CourseID = 3141, Title = "Trigonometry", Credits = 4,
DepartmentID = departments.Single( s => s.Name == "Mathematics").DepartmentID
},
new Course {CourseID = 2021, Title = "Composition", Credits = 3,
DepartmentID = departments.Single( s => s.Name == "English").DepartmentID
},
new Course {CourseID = 2042, Title = "Literature", Credits = 4,
DepartmentID = departments.Single( s => s.Name == "English").DepartmentID
},
};
foreach (Course c in courses)
{
context.Courses.Add(c);
}
context.SaveChanges();
var officeAssignments = new OfficeAssignment[]
{
new OfficeAssignment {
InstructorID = instructors.Single( i => i.LastName == "Fakhouri").ID,
Location = "Smith 17" },
new OfficeAssignment {
InstructorID = instructors.Single( i => i.LastName == "Harui").ID,
Location = "Gowan 27" },
new OfficeAssignment {
InstructorID = instructors.Single( i => i.LastName == "Kapoor").ID,
Location = "Thompson 304" },
};
foreach (OfficeAssignment o in officeAssignments)
{
context.OfficeAssignments.Add(o);
}
context.SaveChanges();
var courseInstructors = new CourseAssignment[]
{
new CourseAssignment {
CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID,
InstructorID = instructors.Single(i => i.LastName == "Kapoor").ID
},
new CourseAssignment {
CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID,
InstructorID = instructors.Single(i => i.LastName == "Harui").ID
},
new CourseAssignment {
CourseID = courses.Single(c => c.Title == "Microeconomics" ).CourseID,
InstructorID = instructors.Single(i => i.LastName == "Zheng").ID
},
new CourseAssignment {
CourseID = courses.Single(c => c.Title == "Macroeconomics" ).CourseID,
InstructorID = instructors.Single(i => i.LastName == "Zheng").ID
},
new CourseAssignment {
CourseID = courses.Single(c => c.Title == "Calculus" ).CourseID,
InstructorID = instructors.Single(i => i.LastName == "Fakhouri").ID
},
new CourseAssignment {
CourseID = courses.Single(c => c.Title == "Trigonometry" ).CourseID,
InstructorID = instructors.Single(i => i.LastName == "Harui").ID
},
new CourseAssignment {
CourseID = courses.Single(c => c.Title == "Composition" ).CourseID,
InstructorID = instructors.Single(i => i.LastName == "Abercrombie").ID
},
new CourseAssignment {
CourseID = courses.Single(c => c.Title == "Literature" ).CourseID,
InstructorID = instructors.Single(i => i.LastName == "Abercrombie").ID
},
};
foreach (CourseAssignment ci in courseInstructors)
{
context.CourseAssignments.Add(ci);
}
context.SaveChanges();
var enrollments = new Enrollment[]
{
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alexander").ID,
CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID,
Grade = Grade.A
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alexander").ID,
CourseID = courses.Single(c => c.Title == "Microeconomics" ).CourseID,
Grade = Grade.C
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alexander").ID,
CourseID = courses.Single(c => c.Title == "Macroeconomics" ).CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alonso").ID,
CourseID = courses.Single(c => c.Title == "Calculus" ).CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alonso").ID,
CourseID = courses.Single(c => c.Title == "Trigonometry" ).CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alonso").ID,
CourseID = courses.Single(c => c.Title == "Composition" ).CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Anand").ID,
CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Anand").ID,
CourseID = courses.Single(c => c.Title == "Microeconomics").CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Barzdukas").ID,
CourseID = courses.Single(c => c.Title == "Chemistry").CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Li").ID,
CourseID = courses.Single(c => c.Title == "Composition").CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Justice").ID,
CourseID = courses.Single(c => c.Title == "Literature").CourseID,
Grade = Grade.B
}
};
foreach (Enrollment e in enrollments)
{
var enrollmentInDataBase = context.Enrollments.Where(
s =>
s.Student.ID == e.StudentID &&
s.Course.CourseID == e.CourseID).SingleOrDefault();
if (enrollmentInDataBase == null)
{
context.Enrollments.Add(e);
}
}
context.SaveChanges();
}
}
}
Seperti yang Anda lihat di tutorial pertama, sebagian besar kode ini hanya membuat objek entitas baru dan memuat data sampel ke dalam properti sebagaimana diperlukan untuk pengujian. Perhatikan bagaimana hubungan banyak-ke-banyak ditangani: kode membuat hubungan dengan membuat entitas di Enrollments
dan CourseAssignment
menggabungkan set entitas.
Menambahkan migrasi
Simpan perubahan Anda dan bangun proyek. Kemudian buka jendela perintah di folder proyek dan masukkan migrations add
perintah (jangan lakukan perintah update-database):
dotnet ef migrations add ComplexDataModel
Anda mendapatkan peringatan tentang kemungkinan kehilangan data.
An operation was scaffolded that may result in the loss of data. Please review the migration for accuracy.
Done. To undo this action, use 'ef migrations remove'
Jika Anda mencoba menjalankan database update
perintah pada saat ini (jangan melakukannya), Anda akan mendapatkan kesalahan berikut:
Pernyataan ALTER TABLE berkonflik dengan batasan KUNCI ASING "FK_dbo. Course_dbo. Department_DepartmentID". Konflik terjadi dalam database "ContosoUniversity", tabel "dbo. Departemen", kolom 'DepartmentID'.
Terkadang ketika Anda menjalankan migrasi dengan data yang ada, Anda perlu menyisipkan data stub ke dalam database untuk memenuhi batasan kunci asing. Kode yang dihasilkan dalam Up
metode menambahkan kunci Course
asing yang tidak dapat DepartmentID
diubah ke tabel. Jika sudah ada baris dalam tabel Kursus saat kode berjalan, AddColumn
operasi gagal karena SQL Server tidak tahu nilai apa yang harus dimasukkan ke dalam kolom yang tidak boleh null. Untuk tutorial ini, Anda akan menjalankan migrasi pada database baru, tetapi dalam aplikasi produksi Anda harus membuat migrasi menangani data yang ada, sehingga petunjuk berikut menunjukkan contoh cara melakukannya.
Untuk membuat migrasi ini berfungsi dengan data yang ada, Anda harus mengubah kode untuk memberi kolom baru nilai default, dan membuat departemen stub bernama "Temp" untuk bertindak sebagai departemen default. Akibatnya, baris Kursus yang ada semuanya akan terkait dengan departemen "Temp" setelah Up
metode berjalan.
Buka file
{timestamp}_ComplexDataModel.cs
.Komentari baris kode yang menambahkan kolom DepartmentID ke tabel Kursus.
migrationBuilder.AlterColumn<string>( name: "Title", table: "Course", maxLength: 50, nullable: true, oldClrType: typeof(string), oldNullable: true); //migrationBuilder.AddColumn<int>( // name: "DepartmentID", // table: "Course", // nullable: false, // defaultValue: 0);
Tambahkan kode yang disorot berikut setelah kode yang membuat tabel Departemen:
migrationBuilder.CreateTable( name: "Department", columns: table => new { DepartmentID = table.Column<int>(nullable: false) .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), Budget = table.Column<decimal>(type: "money", nullable: false), InstructorID = table.Column<int>(nullable: true), Name = table.Column<string>(maxLength: 50, nullable: true), StartDate = table.Column<DateTime>(nullable: false) }, constraints: table => { table.PrimaryKey("PK_Department", x => x.DepartmentID); table.ForeignKey( name: "FK_Department_Instructor_InstructorID", column: x => x.InstructorID, principalTable: "Instructor", principalColumn: "ID", onDelete: ReferentialAction.Restrict); }); migrationBuilder.Sql("INSERT INTO dbo.Department (Name, Budget, StartDate) VALUES ('Temp', 0.00, GETDATE())"); // Default value for FK points to department created above, with // defaultValue changed to 1 in following AddColumn statement. migrationBuilder.AddColumn<int>( name: "DepartmentID", table: "Course", nullable: false, defaultValue: 1);
Dalam aplikasi produksi, Anda akan menulis kode atau skrip untuk menambahkan baris Departemen dan menghubungkan baris Kursus ke baris Departemen baru. Anda kemudian tidak akan lagi memerlukan departemen "Temp" atau nilai default pada Course.DepartmentID
kolom.
Simpan perubahan Anda dan bangun proyek.
Mengubah string koneksi
Anda sekarang memiliki kode baru di DbInitializer
kelas yang menambahkan data benih untuk entitas baru ke database kosong. Untuk membuat EF membuat database kosong baru, ubah nama database di string koneksi appsettings.json
menjadi ContosoUniversity3 atau beberapa nama lain yang belum Anda gunakan di komputer yang Anda gunakan.
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity3;Trusted_Connection=True;MultipleActiveResultSets=true"
},
Simpan perubahan Anda ke appsettings.json
.
Catatan
Sebagai alternatif untuk mengubah nama database, Anda bisa menghapus database. Gunakan SQL Server Object Explorer (SSOX) atau database drop
perintah CLI:
dotnet ef database drop
Memperbarui database
Setelah Anda mengubah nama database atau menghapus database, jalankan database update
perintah di jendela perintah untuk menjalankan migrasi.
dotnet ef database update
Jalankan aplikasi untuk menyebabkan DbInitializer.Initialize
metode berjalan dan mengisi database baru.
Buka database di SSOX seperti yang Anda lakukan sebelumnya, dan perluas simpul Tabel untuk melihat bahwa semua tabel telah dibuat. (Jika Anda masih membuka SSOX dari waktu sebelumnya, klik Tombol Refresh .)
Jalankan aplikasi untuk memicu kode penginisialisasi yang menyemai database.
Klik kanan tabel CourseAssignment dan pilih Tampilkan Data untuk memverifikasi bahwa ia memiliki data di dalamnya.
Mendapatkan kode
Unduh atau lihat aplikasi yang telah selesai.
Langkah berikutnya
Di tutorial ini, Anda akan:
- Mengkustomisasi model Data
- Membuat perubahan pada entitas Siswa
- Entitas Instruktur yang Dibuat
- Entitas OfficeAssignment yang dibuat
- Entitas Kursus yang Dimodifikasi
- Entitas Departemen yang Dibuat
- Entitas Pendaftaran yang Dimodifikasi
- Memperbarui konteks database
- Database benih dengan data pengujian
- Menambahkan migrasi
- Mengubah string koneksi
- Memperbarui database
Lanjutkan ke tutorial berikutnya untuk mempelajari selengkapnya tentang cara mengakses data terkait.
ASP.NET Core