Bagikan melalui


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:

School_class_diagram

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 DataTypeDisplayFormat 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.

Students_index_page_with_formatted_date

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.

Dua cuplikan layar yang memperlihatkan perbedaan dalam Nama dan Jenis Data dari dua tabel Siswa.

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 dan Department 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 untuk Instructor entitas, dan tanda tanya ditambahkan setelah int penunjukan jenis untuk menandai properti sebagai nullable. Properti navigasi diberi nama Administrator tetapi menyimpan Instructor 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 mengharuskan InstructorID 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 dan Course 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 dan Student 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.)

Course_many many_relationship siswa

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:

Instruktur-Course_many-ke-many_relationship

Tabel gabungan diperlukan dalam database, namun, seperti yang diperlihatkan dalam diagram database berikut:

Instruktur-Course_many-ke-many_relationship_tables

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.

School_data_model_diagram

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 dan Course , 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 seperti InstructorInstructorID 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 .)

Cuplikan layar yang memperlihatkan jendela Penjelajah Server. Folder Tabel di bawah Konteks Sekolah terbuka.

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.

Table_data_in_CourseInstructor_table

Mendapatkan kode

Unduh Proyek yang Selesai

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.