Tutorial: Membuat model data yang lebih kompleks untuk aplikasi MVC ASP.NET
Dalam tutorial sebelumnya Anda bekerja dengan model data sederhana yang terdiri dari tiga entitas. Dalam tutorial ini Anda menambahkan lebih banyak entitas dan hubungan dan Anda menyesuaikan model data dengan menentukan aturan pemformatan, validasi, dan pemetaan database. Artikel ini memperlihatkan dua cara untuk menyesuaikan model data: dengan menambahkan atribut ke kelas entitas dan dengan menambahkan kode ke kelas konteks database.
Setelah selesai, kelas entitas akan membentuk model data lengkap yang ditampilkan dalam ilustrasi berikut:
Di tutorial ini, Anda akan:
- Mengkustomisasi model data
- Memperbarui entitas Siswa
- Membuat entitas Instruktur
- Membuat entitas OfficeAssignment
- Mengubah entitas Kursus
- Membuat entitas Departemen
- Mengubah entitas Pendaftaran
- Menambahkan kode ke konteks database
- Database benih dengan data pengujian
- Menambahkan migrasi
- 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 lengkap School
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 virtual 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. Enumerasi DataType menyediakan banyak jenis data, seperti Tanggal, Waktu, Nomor Telepon, 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 DataType memancarkan atribut data HTML 5 - (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)]
ApplyFormatInEditMode
Pengaturan menentukan bahwa pemformatan yang ditentukan 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 pengeditan.)
Anda dapat menggunakan atribut DisplayFormat dengan sendirinya, tetapi umumnya merupakan ide yang baik untuk menggunakan atribut DataType 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 lokal, tautan email, beberapa validasi input sisi klien, dll.).
- Secara default, browser akan merender data menggunakan format yang benar berdasarkan lokal Anda.
- Atribut DataType dapat mengaktifkan MVC untuk memilih templat bidang yang tepat untuk merender data ( DisplayFormat menggunakan templat string). Untuk informasi selengkapnya, lihat Templat MVC 2 ASP.NET Brad Wilson. (Meskipun ditulis untuk MVC 2, artikel ini masih berlaku untuk versi ASP.NET MVC saat ini.)
Jika Anda menggunakan DataType
atribut dengan bidang tanggal, Anda harus menentukan DisplayFormat
atribut juga untuk memastikan bahwa bidang dirender dengan benar di browser Chrome. Untuk informasi selengkapnya, lihat utas StackOverflow ini.
Untuk informasi selengkapnya tentang cara menangani format tanggal lain di MVC, buka Pengantar MVC 5: Memeriksa Metode Edit dan Edit Tampilan dan cari di halaman untuk "internasionalisasi".
Jalankan halaman Indeks Siswa lagi dan perhatikan bahwa waktu tidak lagi ditampilkan untuk tanggal pendaftaran. Hal yang sama akan berlaku untuk tampilan apa pun yang menggunakan Student
model.
The StringLengthAttribute
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 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 atribut StringLength 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, ErrorMessage = "First name cannot be longer than 50 characters.")]
public string FirstMidName { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
}
Atribut StringLength tidak akan mencegah pengguna memasukkan spasi kosong untuk nama. Anda dapat menggunakan atribut RegularExpression 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""'\s-]*$")]
Atribut MaxLength menyediakan fungsionalitas serupa dengan atribut StringLength tetapi tidak memberikan validasi sisi klien.
Jalankan aplikasi dan klik tab Siswa . Anda mendapatkan kesalahan berikut:
Model yang mendukung konteks 'SchoolContext' telah berubah sejak database dibuat. Pertimbangkan untuk menggunakan Migrasi Pertama Kode untuk memperbarui database (https://go.microsoft.com/fwlink/?LinkId=238269).
Model database telah berubah dengan cara yang memerlukan perubahan dalam skema database, dan Kerangka Kerja Entitas mendeteksinya. Anda akan menggunakan migrasi untuk memperbarui skema tanpa kehilangan data apa pun yang Anda tambahkan ke database dengan menggunakan UI. Jika Anda mengubah data yang dibuat oleh Seed
metode , yang akan diubah kembali ke keadaan semula karena metode AddOrUpdate yang Anda gunakan dalam Seed
metode . (AddOrUpdate setara dengan operasi "upsert" dari terminologi database.)
Di Package Manager Console (PMC), masukkan perintah berikut:
add-migration MaxLengthOnNames
update-database
Perintah add-migration
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 update-database
menjalankan kode itu.
Tanda waktu yang ditambahkan sebelumnya ke nama file migrasi digunakan oleh Kerangka Kerja Entitas untuk memesan migrasi. Anda dapat membuat beberapa migrasi sebelum menjalankan update-database
perintah, lalu semua migrasi diterapkan dalam urutan pembuatannya.
Jalankan halaman Buat , dan masukkan nama yang lebih panjang dari 50 karakter. Saat Anda mengklik Buat, validasi sisi klien memperlihatkan pesan kesalahan: Bidang LastName harus berupa string dengan panjang maksimum 50.
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 atau diperbarui di FirstName
kolom Student
tabel. Jika Anda tidak menentukan nama kolom, nama kolom akan diberi nama yang sama dengan nama properti.
Dalam file Student.cs , tambahkan using
pernyataan untuk System.ComponentModel.DataAnnotations.Schema dan tambahkan atribut nama kolom ke FirstMidName
properti , seperti yang 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, ErrorMessage = "First name cannot be longer than 50 characters.")]
[Column("FirstName")]
public string FirstMidName { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
}
Penambahan atribut Kolom mengubah model yang mendukung SchoolContext, sehingga tidak akan cocok dengan database. Masukkan perintah berikut di PMC untuk membuat migrasi lain:
add-migration ColumnFirstName
update-database
Di Server Explorer, buka perancang tabel Siswa dengan mengklik dua kali tabel Siswa .
Gambar berikut menunjukkan nama kolom asli seperti sebelum Anda menerapkan dua migrasi pertama. Selain nama kolom yang berubah dari FirstMidName
ke FirstName
, dua kolom nama telah berubah dari MAX
panjang menjadi 50 karakter.
Anda juga dapat membuat perubahan pemetaan database menggunakan FLUENT API, seperti yang akan Anda lihat nanti dalam tutorial ini.
Catatan
Jika Anda mencoba mengkompilasi sebelum selesai membuat semua kelas entitas di bagian berikut, Anda mungkin mendapatkan kesalahan pengkompilasi.
Memperbarui 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, ErrorMessage = "First name cannot be longer than 50 characters.")]
[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 virtual ICollection<Enrollment> Enrollments { get; set; }
}
}
Atribut yang Diperlukan
Atribut Wajib membuat properti nama menjadi bidang yang diperlukan. Required attribute
tidak diperlukan untuk jenis nilai seperti DateTime, int, double, dan float. Jenis nilai tidak dapat ditetapkan nilai null, sehingga secara inheren diperlakukan sebagai bidang yang diperlukan.
Atribut Required
harus digunakan dengan MinimumLength
agar MinimumLength
diberlakukan.
[Display(Name = "Last Name")]
[Required]
[StringLength(50, MinimumLength=2)]
public string LastName { get; set; }
MinimumLength
dan Required
izinkan spasi kosong untuk memenuhi validasi. RegularExpression
Gunakan atribut untuk kontrol penuh atas string.
Atribut Tampilan
Atribut Display
menentukan bahwa caption 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 get
aksesor, dan tidak ada FullName
kolom yang akan dihasilkan dalam database.
Membuat entitas Instruktur
Buat Models\Instructor.cs, ganti 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 virtual ICollection<Course> Courses { get; set; }
public virtual OfficeAssignment OfficeAssignment { get; set; }
}
}
Perhatikan bahwa beberapa properti sama di Student
entitas dan Instructor
. 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 kelas instruktur sebagai berikut:
public class Instructor
{
public int ID { get; set; }
[Display(Name = "Last Name"),StringLength(50, MinimumLength=1)]
public string LastName { get; set; }
[Column("FirstName"),Display(Name = "First Name"),StringLength(50, MinimumLength=1)]
public string FirstMidName { get; set; }
[DataType(DataType.Date),Display(Name = "Hire Date"),DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime HireDate { get; set; }
[Display(Name = "Full Name")]
public string FullName
{
get { return LastName + ", " + FirstMidName; }
}
public virtual ICollection<Course> Courses { get; set; }
public virtual OfficeAssignment OfficeAssignment { get; set; }
}
Kursus dan Properti Navigasi OfficeAssignment
Properti Courses
dan OfficeAssignment
adalah properti navigasi. Seperti yang dijelaskan sebelumnya, mereka biasanya didefinisikan sebagai virtual sehingga mereka dapat memanfaatkan fitur Kerangka Kerja Entitas yang disebut pemuatan malas. Selain itu, jika properti navigasi dapat menampung beberapa entitas, jenisnya harus mengimplementasikan Antarmuka T> ICollection<. Misalnya IList<T> memenuhi syarat tetapi tidak IEnumerable<T> karena IEnumerable<T>
tidak menerapkan Tambahkan.
Instruktur dapat mengajarkan sejumlah kursus, sehingga Courses
didefinisikan sebagai kumpulan Course
entitas.
public virtual ICollection<Course> Courses { get; set; }
Aturan bisnis kami menyatakan instruktur hanya dapat memiliki paling banyak satu kantor, sehingga OfficeAssignment
didefinisikan sebagai entitas tunggal OfficeAssignment
(yang mungkin null
jika tidak ada kantor yang ditetapkan).
public virtual 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]
[ForeignKey("Instructor")]
public int InstructorID { get; set; }
[StringLength(50)]
[Display(Name = "Office Location")]
public string Location { get; set; }
public virtual Instructor Instructor { get; set; }
}
}
Bangun proyek, yang menyimpan perubahan Anda dan memverifikasi bahwa Anda belum membuat kesalahan salin dan tempel yang dapat ditangkap kompilator.
Atribut Kunci
Ada hubungan satu-ke-nol-atau-satu antara Instructor
entitas dan OfficeAssignment
. Penugasan kantor hanya ada dalam kaitannya dengan instruktur yang ditetapkan, 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 namaID
kelas. Oleh karena itu, Key
atribut digunakan untuk mengidentifikasinya sebagai kunci:
[Key]
[ForeignKey("Instructor")]
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 yang berbeda dari classnameID
atau ID
. Secara default EF memperlakukan kunci sebagai non-database yang dihasilkan karena kolom adalah untuk hubungan identifikasi.
Atribut ForeignKey
Ketika ada hubungan satu-ke-nol-atau-satu atau hubungan satu-ke-satu antara dua entitas (seperti antara OfficeAssignment
dan Instructor
), EF tidak dapat mengetahui ujung hubungan mana yang menjadi prinsipal dan ujung mana yang bergantung. Hubungan satu ke satu memiliki properti navigasi referensi di setiap kelas ke kelas lainnya. Atribut ForeignKey dapat diterapkan ke kelas dependen untuk membangun hubungan. Jika Anda menghilangkan Atribut ForeignKey, Anda mendapatkan kesalahan berikut saat mencoba membuat migrasi:
Tidak dapat menentukan akhir utama hubungan antara jenis 'ContosoUniversity.Models.OfficeAssignment' dan 'ContosoUniversity.Models.Instructor'. Akhir utama asosiasi ini harus dikonfigurasi secara eksplisit menggunakan API fasih hubungan atau anotasi data.
Nanti dalam tutorial, Anda akan melihat cara mengonfigurasi hubungan ini dengan API yang fasih.
Properti Navigasi Instruktur
Entitas Instructor
memiliki properti navigasi yang dapat diubah OfficeAssignment
ke null (karena instruktur mungkin tidak memiliki penugasan kantor), dan OfficeAssignment
entitas memiliki properti navigasi yang tidak dapat Instructor
diubah ke null (karena penetapan kantor tidak dapat ada tanpa instruktur -- InstructorID
tidak dapat diubah ke null). Instructor
Ketika entitas memiliki entitas terkaitOfficeAssignment
, setiap entitas akan memiliki referensi ke entitas lain 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 kunci asing InstructorID (yang juga merupakan kunci untuk tabel ini) tidak dapat diubah ke null.
Mengubah entitas Kursus
Di Models\Course.cs, ganti kode yang Anda tambahkan sebelumnya dengan kode berikut:
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 virtual Department Department { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
public virtual ICollection<Instructor> Instructors { get; set; }
}
}
Entitas kursus memiliki properti DepartmentID
kunci asing yang menunjuk ke entitas terkait Department
dan memiliki Department
properti navigasi. Kerangka Kerja Entitas tidak mengharuskan Anda untuk 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. Tetapi memiliki kunci asing dalam model data dapat membuat pembaruan lebih sederhana dan lebih efisien. Misalnya, ketika Anda mengambil entitas kursus untuk diedit, Department
entitas null jika Anda tidak memuatnya, jadi ketika Anda memperbarui entitas kursus, Anda harus terlebih Department
dahulu mengambil entitas. Saat properti DepartmentID
kunci asing disertakan dalam model data, Anda tidak perlu mengambil Department
entitas sebelum memperbarui.
Atribut DatabaseGenerated
Atribut DatabaseGenerated dengan parameter None pada CourseID
properti menentukan bahwa nilai kunci primer disediakan oleh pengguna daripada yang dihasilkan oleh database.
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Display(Name = "Number")]
public int CourseID { get; set; }
Secara default, Kerangka Kerja Entitas 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.
Properti Kunci dan Navigasi Asing
Properti kunci asing dan properti navigasi dalam Course
entitas mencerminkan hubungan berikut:
Kursus ditugaskan ke satu departemen, jadi ada
DepartmentID
kunci asing danDepartment
properti navigasi karena alasan yang disebutkan di atas.public int DepartmentID { get; set; } public virtual Department Department { get; set; }
Kursus dapat memiliki sejumlah siswa yang terdaftar di dalamnya, sehingga
Enrollments
properti navigasi adalah koleksi:public virtual ICollection<Enrollment> Enrollments { get; set; }
Kursus dapat diajarkan oleh beberapa instruktur, sehingga
Instructors
properti navigasi adalah koleksi:public virtual ICollection<Instructor> Instructors { 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 virtual Instructor Administrator { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
}
Atribut Kolom
Sebelumnya Anda menggunakan atribut Kolom 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 uang SQL Server dalam database:
[Column(TypeName="money")]
public decimal Budget { get; set; }
Pemetaan kolom umumnya tidak diperlukan, karena Kerangka Kerja Entitas biasanya memilih jenis data SQL Server yang sesuai berdasarkan jenis CLR yang Anda tentukan untuk properti . Jenis CLR decimal
memetakan ke jenis SQL Serverdecimal
. Tetapi dalam hal ini Anda tahu bahwa kolom akan menyimpan jumlah mata uang, dan jenis data uang lebih sesuai untuk itu. Untuk informasi selengkapnya tentang jenis data CLR dan bagaimana kecocokannya dengan SQL Server jenis data, lihat SqlClient untuk Entity FrameworkTypes.
Properti Kunci dan Navigasi Asing
Properti kunci dan navigasi asing mencerminkan hubungan berikut:
Departemen mungkin atau mungkin tidak memiliki administrator, dan administrator selalu menjadi instruktur.
InstructorID
Oleh karena itu properti disertakan sebagai kunci asing untukInstructor
entitas, dan tanda tanya ditambahkan setelahint
penunjukan jenis untuk menandai properti sebagai nullable. Properti navigasi diberi namaAdministrator
tetapi menyimpanInstructor
entitas:public int? InstructorID { get; set; } public virtual Instructor Administrator { get; set; }
Departemen mungkin memiliki banyak kursus, jadi ada
Courses
properti navigasi:public virtual 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 saat Anda mencoba menambahkan migrasi. Misalnya, jika Anda tidak menentukan
Department.InstructorID
properti sebagai nullable, Anda akan mendapatkan pesan pengecualian berikut: "Hubungan referensial akan menghasilkan referensi siklus yang tidak diizinkan." Jika aturan bisnis Anda mengharuskanInstructorID
properti tidak dapat diubah ke null, Anda harus menggunakan pernyataan API fasih berikut untuk menonaktifkan penghapusan kaskade pada hubungan:
modelBuilder.Entity().HasRequired(d => d.Administrator).WithMany().WillCascadeOnDelete(false);
Mengubah entitas Pendaftaran
Di Models\Enrollment.cs, ganti kode yang Anda tambahkan sebelumnya dengan kode berikut
using System.ComponentModel.DataAnnotations;
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 virtual Course Course { get; set; }
public virtual 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 danCourse
properti navigasi:public int CourseID { get; set; } public virtual Course Course { get; set; }
Catatan pendaftaran adalah untuk satu siswa, jadi ada
StudentID
properti kunci asing danStudent
properti navigasi:public int StudentID { get; set; } public virtual 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. Ini 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; membuat diagram bukan bagian dari tutorial, diagram ini hanya digunakan di sini sebagai ilustrasi.)
Setiap baris 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 sesuai dengan tabel gabungan banyak ke banyak tanpa payload (atau tabel gabungan murni) dalam database, dan Anda tidak perlu membuat kelas model untuk itu sama sekali. Entitas Instructor
dan Course
memiliki hubungan banyak ke banyak seperti itu, dan seperti yang Anda lihat, tidak ada kelas entitas di antara mereka:
Tabel gabungan diperlukan dalam database, namun, seperti yang diperlihatkan dalam diagram database berikut:
Kerangka Kerja Entitas secara otomatis membuat CourseInstructor
tabel, dan Anda membaca dan memperbaruinya secara tidak langsung dengan membaca dan memperbarui Instructor.Courses
properti navigasi dan Course.Instructors
.
Diagram hubungan entitas
Ilustrasi berikut menunjukkan diagram yang dibuat Entity Framework Power Tools untuk model Sekolah yang telah selesai.
Selain garis hubungan banyak ke banyak (* ke *) dan 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 hingga *) antara entitas Instruktur dan Departemen.
Menambahkan kode ke konteks database
Selanjutnya Anda akan menambahkan entitas baru ke SchoolContext
kelas dan menyesuaikan beberapa pemetaan menggunakan panggilan API yang fasih . API "fasih" karena sering digunakan dengan merangkai serangkaian panggilan metode bersama-sama ke dalam satu pernyataan, seperti dalam contoh berikut:
modelBuilder.Entity<Course>()
.HasMany(c => c.Instructors).WithMany(i => i.Courses)
.Map(t => t.MapLeftKey("CourseID")
.MapRightKey("InstructorID")
.ToTable("CourseInstructor"));
Dalam tutorial ini Anda akan menggunakan API yang fasih hanya untuk pemetaan database yang tidak dapat Anda lakukan dengan atribut. Namun, Anda juga dapat menggunakan API yang 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 fasih jika mau, dan ada beberapa penyesuaian yang hanya dapat dilakukan dengan menggunakan API yang fasih, tetapi secara umum praktik yang disarankan adalah memilih salah satu dari dua pendekatan ini dan menggunakannya secara konsisten sebanyak mungkin.
Untuk menambahkan entitas baru ke model data dan melakukan pemetaan database yang tidak Anda lakukan dengan menggunakan atribut, ganti kode di DAL\SchoolContext.cs dengan kode berikut:
using ContosoUniversity.Models;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
namespace ContosoUniversity.DAL
{
public class SchoolContext : DbContext
{
public DbSet<Course> Courses { get; set; }
public DbSet<Department> Departments { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Instructor> Instructors { get; set; }
public DbSet<Student> Students { get; set; }
public DbSet<OfficeAssignment> OfficeAssignments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Course>()
.HasMany(c => c.Instructors).WithMany(i => i.Courses)
.Map(t => t.MapLeftKey("CourseID")
.MapRightKey("InstructorID")
.ToTable("CourseInstructor"));
}
}
}
Pernyataan baru dalam metode OnModelCreating mengonfigurasi tabel gabungan banyak ke banyak:
Untuk hubungan banyak ke banyak antara
Instructor
entitas danCourse
, kode menentukan nama tabel dan kolom untuk tabel gabungan. Code First dapat mengonfigurasi hubungan banyak-ke-banyak untuk Anda tanpa kode ini, tetapi jika Anda tidak memanggilnya, Anda akan mendapatkan nama default sepertiInstructorInstructorID
untuk kolom.InstructorID
modelBuilder.Entity<Course>() .HasMany(c => c.Instructors).WithMany(i => i.Courses) .Map(t => t.MapLeftKey("CourseID") .MapRightKey("InstructorID") .ToTable("CourseInstructor"));
Kode berikut memberikan contoh bagaimana Anda dapat menggunakan API yang fasih alih-alih atribut untuk menentukan hubungan antara Instructor
entitas dan OfficeAssignment
:
modelBuilder.Entity<Instructor>()
.HasOptional(p => p.OfficeAssignment).WithRequired(p => p.Instructor);
Untuk informasi tentang pernyataan "API fasih" apa yang dilakukan di belakang layar, lihat posting blog API Fasih .
Seed database dengan data pengujian
Ganti kode dalam file Migrations\Configuration.cs dengan kode berikut untuk menyediakan data nilai awal untuk entitas baru yang telah Anda buat.
namespace ContosoUniversity.Migrations
{
using ContosoUniversity.Models;
using ContosoUniversity.DAL;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;
internal sealed class Configuration : DbMigrationsConfiguration<SchoolContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(SchoolContext context)
{
var students = new List<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") }
};
students.ForEach(s => context.Students.AddOrUpdate(p => p.LastName, s));
context.SaveChanges();
var instructors = new List<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") }
};
instructors.ForEach(s => context.Instructors.AddOrUpdate(p => p.LastName, s));
context.SaveChanges();
var departments = new List<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 }
};
departments.ForEach(s => context.Departments.AddOrUpdate(p => p.Name, s));
context.SaveChanges();
var courses = new List<Course>
{
new Course {CourseID = 1050, Title = "Chemistry", Credits = 3,
DepartmentID = departments.Single( s => s.Name == "Engineering").DepartmentID,
Instructors = new List<Instructor>()
},
new Course {CourseID = 4022, Title = "Microeconomics", Credits = 3,
DepartmentID = departments.Single( s => s.Name == "Economics").DepartmentID,
Instructors = new List<Instructor>()
},
new Course {CourseID = 4041, Title = "Macroeconomics", Credits = 3,
DepartmentID = departments.Single( s => s.Name == "Economics").DepartmentID,
Instructors = new List<Instructor>()
},
new Course {CourseID = 1045, Title = "Calculus", Credits = 4,
DepartmentID = departments.Single( s => s.Name == "Mathematics").DepartmentID,
Instructors = new List<Instructor>()
},
new Course {CourseID = 3141, Title = "Trigonometry", Credits = 4,
DepartmentID = departments.Single( s => s.Name == "Mathematics").DepartmentID,
Instructors = new List<Instructor>()
},
new Course {CourseID = 2021, Title = "Composition", Credits = 3,
DepartmentID = departments.Single( s => s.Name == "English").DepartmentID,
Instructors = new List<Instructor>()
},
new Course {CourseID = 2042, Title = "Literature", Credits = 4,
DepartmentID = departments.Single( s => s.Name == "English").DepartmentID,
Instructors = new List<Instructor>()
},
};
courses.ForEach(s => context.Courses.AddOrUpdate(p => p.CourseID, s));
context.SaveChanges();
var officeAssignments = new List<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" },
};
officeAssignments.ForEach(s => context.OfficeAssignments.AddOrUpdate(p => p.InstructorID, s));
context.SaveChanges();
AddOrUpdateInstructor(context, "Chemistry", "Kapoor");
AddOrUpdateInstructor(context, "Chemistry", "Harui");
AddOrUpdateInstructor(context, "Microeconomics", "Zheng");
AddOrUpdateInstructor(context, "Macroeconomics", "Zheng");
AddOrUpdateInstructor(context, "Calculus", "Fakhouri");
AddOrUpdateInstructor(context, "Trigonometry", "Harui");
AddOrUpdateInstructor(context, "Composition", "Abercrombie");
AddOrUpdateInstructor(context, "Literature", "Abercrombie");
context.SaveChanges();
var enrollments = new List<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();
}
void AddOrUpdateInstructor(SchoolContext context, string courseTitle, string instructorName)
{
var crs = context.Courses.SingleOrDefault(c => c.Title == courseTitle);
var inst = crs.Instructors.SingleOrDefault(i => i.LastName == instructorName);
if (inst == null)
crs.Instructors.Add(context.Instructors.Single(i => i.LastName == instructorName));
}
}
}
Seperti yang Anda lihat di tutorial pertama, sebagian besar kode ini hanya memperbarui atau membuat objek entitas baru dan memuat data sampel ke dalam properti sebagaimana diperlukan untuk pengujian. Namun, perhatikan bagaimana Course
entitas, yang memiliki hubungan banyak ke banyak dengan Instructor
entitas, ditangani:
var courses = new List<Course>
{
new Course {CourseID = 1050, Title = "Chemistry", Credits = 3,
DepartmentID = departments.Single( s => s.Name == "Engineering").DepartmentID,
Instructors = new List<Instructor>()
},
...
};
courses.ForEach(s => context.Courses.AddOrUpdate(p => p.CourseID, s));
context.SaveChanges();
Saat Anda membuat Course
objek, Anda menginisialisasi Instructors
properti navigasi sebagai koleksi kosong menggunakan kode Instructors = new List<Instructor>()
. Ini memungkinkan untuk menambahkan Instructor
entitas yang terkait dengan ini Course
dengan menggunakan Instructors.Add
metode . Jika Anda tidak membuat daftar kosong, Anda tidak akan dapat menambahkan hubungan ini, karena Instructors
properti akan null dan tidak akan memiliki Add
metode. Anda juga dapat menambahkan inisialisasi daftar ke konstruktor.
Menambahkan migrasi
Dari PMC, masukkan add-migration
perintah (jangan lakukan update-database
perintah):
add-Migration ComplexDataModel
Jika Anda mencoba menjalankan update-database
perintah pada saat ini (jangan lakukan), Anda akan mendapatkan kesalahan berikut:
Pernyataan ALTER TABLE berkonflik dengan batasan FOREIGN KEY "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, dan itulah yang harus Anda lakukan sekarang. Kode yang dihasilkan dalam metode ComplexDataModel Up
menambahkan kunci asing yang tidak dapat DepartmentID
diubah ke Course
tabel. Karena sudah ada baris dalam Course
tabel saat kode berjalan, AddColumn
operasi akan gagal karena SQL Server tidak tahu nilai apa yang akan dimasukkan ke dalam kolom yang tidak boleh null. Oleh karena itu harus mengubah kode untuk memberi kolom baru nilai default, dan membuat departemen stub bernama "Temp" untuk bertindak sebagai departemen default. Akibatnya, baris yang ada Course
semuanya akan terkait dengan departemen "Temp" setelah Up
metode berjalan. Anda dapat menghubungkannya dengan departemen yang benar dalam metode .Seed
< Edit file tanda waktu>_ComplexDataModel.cs, komentari baris kode yang menambahkan kolom DepartmentID ke tabel Kursus, dan tambahkan kode yang disorot berikut (baris yang dikomentari juga disorot):
CreateTable(
"dbo.CourseInstructor",
c => new
{
CourseID = c.Int(nullable: false),
InstructorID = c.Int(nullable: false),
})
.PrimaryKey(t => new { t.CourseID, t.InstructorID })
.ForeignKey("dbo.Course", t => t.CourseID, cascadeDelete: true)
.ForeignKey("dbo.Instructor", t => t.InstructorID, cascadeDelete: true)
.Index(t => t.CourseID)
.Index(t => t.InstructorID);
// Create a department for course to point to.
Sql("INSERT INTO dbo.Department (Name, Budget, StartDate) VALUES ('Temp', 0.00, GETDATE())");
// default value for FK points to department created above.
AddColumn("dbo.Course", "DepartmentID", c => c.Int(nullable: false, defaultValue: 1));
//AddColumn("dbo.Course", "DepartmentID", c => c.Int(nullable: false));
AlterColumn("dbo.Course", "Title", c => c.String(maxLength: 50));
Seed
Ketika metode berjalan, metode akan menyisipkan baris dalam Department
tabel dan akan menghubungkan baris yang ada Course
dengan baris baru Department
tersebut. Jika Anda belum menambahkan kursus apa pun di UI, Anda tidak akan lagi memerlukan departemen "Temp" atau nilai default pada Course.DepartmentID
kolom. Untuk memungkinkan kemungkinan bahwa seseorang mungkin telah menambahkan kursus dengan menggunakan aplikasi, Anda juga ingin memperbarui Seed
kode metode untuk memastikan bahwa semua Course
baris (bukan hanya yang disisipkan oleh eksekusi metode sebelumnya Seed
) memiliki nilai yang valid DepartmentID
sebelum Anda menghapus nilai default dari kolom dan menghapus departemen "Temp".
Memperbarui database
Setelah Anda selesai mengedit < tanda waktu>_ComplexDataModel.cs, masukkan update-database
perintah di PMC untuk menjalankan migrasi.
update-database
Catatan
Dimungkinkan untuk mendapatkan kesalahan lain saat memigrasikan data dan membuat perubahan skema. Jika Anda mendapatkan kesalahan migrasi yang tidak dapat Anda atasi, Anda bisa mengubah nama database dalam string koneksi atau menghapus database. Pendekatan paling sederhana adalah mengganti nama database dalam file Web.config . Contoh berikut menunjukkan nama yang diubah menjadi CU_Test:
<add name="SchoolContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=CU_Test;Integrated Security=SSPI;"
providerName="System.Data.SqlClient" />
Dengan database baru, tidak ada data untuk dimigrasikan, dan update-database
perintah jauh lebih mungkin diselesaikan tanpa kesalahan. Untuk instruksi tentang cara menghapus database, lihat Cara Menghapus Database dari Visual Studio 2012.
Jika gagal, hal lain yang bisa Anda coba adalah menginisialisasi ulang database dengan memasukkan perintah berikut di PMC:
update-database -TargetMigration:0
Buka database di Server Explorer seperti yang Anda lakukan sebelumnya, dan perluas simpul Tabel untuk melihat bahwa semua tabel telah dibuat. (Jika Anda masih membuka Server Explorer dari waktu sebelumnya, klik tombol Refresh .)
Anda tidak membuat kelas model untuk CourseInstructor
tabel. Seperti yang dijelaskan sebelumnya, ini adalah tabel gabungan untuk hubungan banyak-ke-banyak antara Instructor
entitas dan Course
.
CourseInstructor
Klik kanan tabel dan pilih Perlihatkan Data Tabel untuk memverifikasi bahwa tabel memiliki data di dalamnya sebagai hasil dari entitas yang Instructor
Anda tambahkan ke Course.Instructors
properti navigasi.
Mendapatkan kode
Sumber Daya Tambahan:
Tautan ke sumber daya Kerangka Kerja Entitas lainnya dapat ditemukan di Akses Data ASP.NET - Sumber Daya yang Direkomendasikan.
Langkah berikutnya
Di tutorial ini, Anda akan:
- Mengkustomisasi model data
- Entitas Siswa yang Diperbarui
- Entitas Instruktur yang Dibuat
- Entitas OfficeAssignment yang dibuat
- Memodifikasi entitas Kursus
- Membuat entitas Departemen
- Memodifikasi entitas Pendaftaran
- Menambahkan kode ke konteks database
- Database seeded dengan data pengujian
- Menambahkan migrasi
- Memperbarui database
Lanjutkan ke artikel berikutnya untuk mempelajari cara membaca dan menampilkan data terkait yang dimuat Kerangka Kerja Entitas ke properti navigasi.
Saran dan Komentar
https://aka.ms/ContentUserFeedback.
Segera hadir: Sepanjang tahun 2024 kami akan menghentikan penggunaan GitHub Issues sebagai mekanisme umpan balik untuk konten dan menggantinya dengan sistem umpan balik baru. Untuk mengetahui informasi selengkapnya, lihat:Kirim dan lihat umpan balik untuk