Menerapkan Pewarisan dengan Kerangka Kerja Entitas dalam Aplikasi MVC ASP.NET (8 dari 10)
oleh Tom Dykstra
Aplikasi web sampel Contoso University menunjukkan cara membuat aplikasi ASP.NET MVC 4 menggunakan Entity Framework 5 Code First dan Visual Studio 2012. Untuk informasi tentang seri tutorial, lihat tutorial pertama dalam seri ini.
Catatan
Jika Anda mengalami masalah yang tidak dapat Anda atasi, unduh bab yang telah selesai dan coba reprodurasi masalah Anda. Anda umumnya dapat menemukan solusi untuk masalah dengan membandingkan kode Anda dengan kode yang telah selesai. Untuk beberapa kesalahan umum dan cara mengatasinya, lihat Kesalahan dan Solusi.
Dalam tutorial sebelumnya Anda menangani pengecualian konkurensi. Tutorial ini akan menunjukkan kepada Anda cara menerapkan pewarisan dalam model data.
Dalam pemrograman berorientasi objek, Anda dapat menggunakan pewarisan untuk menghilangkan kode redundan. Dalam tutorial ini, Anda akan mengubah Instructor
kelas dan Student
sehingga mereka berasal dari Person
kelas dasar yang berisi properti seperti LastName
yang umum bagi instruktur dan siswa. Anda tidak akan menambahkan atau mengubah halaman web apa pun, tetapi Anda akan mengubah beberapa kode dan perubahan tersebut akan secara otomatis tercermin dalam database.
Tabel per Hierarki versus Warisan Tabel per Jenis
Dalam pemrograman berorientasi objek, Anda dapat menggunakan warisan untuk mempermudah bekerja dengan kelas terkait. Misalnya, Instructor
kelas dan Student
dalam School
model data berbagi beberapa properti, yang menghasilkan kode redundan:
Misalkan Anda ingin menghilangkan kode redundan untuk properti yang dibagikan oleh Instructor
entitas dan Student
. Anda dapat membuat Person
kelas dasar yang hanya berisi properti bersama tersebut Instructor
, lalu membuat entitas dan Student
mewarisi dari kelas dasar tersebut, seperti yang ditunjukkan dalam ilustrasi berikut:
Ada beberapa cara struktur pewarisan ini dapat diwakili dalam database. Anda bisa memiliki Person
tabel yang menyertakan informasi tentang siswa dan instruktur dalam satu tabel. Beberapa kolom hanya dapat berlaku untuk instruktur (HireDate
), beberapa hanya untuk siswa (EnrollmentDate
), beberapa untuk keduanya (LastName
, FirstName
). Biasanya, Anda akan memiliki kolom diskriminator untuk menunjukkan jenis mana yang diwakili setiap baris. Misalnya, kolom diskriminator mungkin memiliki "Instruktur" untuk instruktur dan "Siswa" untuk siswa.
Pola pembuatan struktur pewarisan entitas dari tabel database tunggal ini disebut pewarisan tabel per hierarki (TPH).
Alternatifnya adalah membuat database terlihat lebih seperti struktur pewarisan. Misalnya, Anda hanya dapat memiliki bidang nama dalam Person
tabel dan memiliki tabel dan Student
terpisah Instructor
dengan bidang tanggal.
Pola pembuatan tabel database untuk setiap kelas entitas ini disebut warisan tabel per jenis (TPT).
Pola pewarisan TPH umumnya memberikan performa yang lebih baik dalam Kerangka Kerja Entitas daripada pola pewarisan TPT, karena pola TPT dapat menghasilkan kueri gabungan yang kompleks. Tutorial ini menunjukkan cara menerapkan pewarisan TPH. Anda akan melakukannya dengan melakukan langkah-langkah berikut:
- Buat
Person
kelas dan ubahInstructor
kelas danStudent
untuk berasal dariPerson
. - Tambahkan kode pemetaan model-ke-database ke kelas konteks database.
- Ubah
InstructorID
danStudentID
referensi di seluruh proyek menjadiPersonID
.
Membuat Kelas Orang
Catatan: Anda tidak akan dapat mengkompilasi proyek setelah membuat kelas di bawah ini hingga Anda memperbarui pengontrol yang menggunakan kelas ini.
Di folder Model , buat Person.cs dan ganti kode templat dengan kode berikut:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public abstract class Person
{
[Key]
public int PersonID { get; set; }
[RegularExpression(@"^[A-Z]+[a-zA-Z""'\s-]*$")]
[StringLength(50, MinimumLength = 1)]
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Column("FirstName")]
[Display(Name = "First Name")]
[StringLength(50, MinimumLength = 2, ErrorMessage = "First name must be between 2 and 50 characters.")]
public string FirstMidName { get; set; }
public string FullName
{
get
{
return LastName + ", " + FirstMidName;
}
}
}
}
Di Instructor.cs, dapatkan Instructor
kelas dari Person
kelas dan hapus bidang kunci dan nama. Kode akan terlihat seperti contoh berikut:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace ContosoUniversity.Models
{
public class Instructor : Person
{
[DataType(DataType.Date)]
[Display(Name = "Hire Date")]
public DateTime HireDate { get; set; }
public virtual ICollection<Course> Courses { get; set; }
public virtual OfficeAssignment OfficeAssignment { get; set; }
}
}
Buat perubahan serupa dengan Student.cs. Kelas Student
akan terlihat seperti contoh berikut:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace ContosoUniversity.Models
{
public class Student : Person
{
[DataType(DataType.Date)]
[Display(Name = "Enrollment Date")]
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
}
Menambahkan Jenis Entitas Orang ke Model
Di SchoolContext.cs, tambahkan DbSet
properti untuk Person
jenis entitas:
public DbSet<Person> People { get; set; }
Ini semua yang dibutuhkan Entity Framework untuk mengonfigurasi warisan tabel per hierarki. Seperti yang akan Anda lihat, saat database dibuat ulang, database akan memiliki Person
tabel sebagai pengganti Student
tabel dan Instructor
.
Mengubah InstructorID dan StudentID ke PersonID
Di SchoolContext.cs, dalam pernyataan pemetaan Instructor-Course, ubah MapRightKey("InstructorID")
menjadi MapRightKey("PersonID")
:
modelBuilder.Entity<Course>()
.HasMany(c => c.Instructors).WithMany(i => i.Courses)
.Map(t => t.MapLeftKey("CourseID")
.MapRightKey("PersonID")
.ToTable("CourseInstructor"));
Perubahan ini tidak diperlukan; itu hanya mengubah nama kolom InstrukturID dalam tabel gabungan banyak-ke-banyak. Jika Anda meninggalkan nama sebagai InstructorID, aplikasi akan tetap berfungsi dengan benar. Berikut adalah SchoolContext.cs yang telah selesai:
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; }
public DbSet<Person> People { 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("PersonID")
.ToTable("CourseInstructor"));
}
}
}
Selanjutnya Anda perlu mengubah InstructorID
ke PersonID
dan StudentID
ke PersonID
di seluruh proyek kecuali dalam file migrasi bertanda waktu di folder Migrasi . Untuk melakukannya, Anda hanya akan menemukan dan membuka file yang perlu diubah, lalu melakukan perubahan global pada file yang dibuka. Satu-satunya file di folder Migrasi yang harus Anda ubah adalah Migrations\Configuration.cs.
-
Penting
Mulailah dengan menutup semua file yang terbuka di Visual Studio.
Klik Temukan dan Ganti -- Temukan semua File di menu Edit , lalu cari semua file dalam proyek yang berisi
InstructorID
.Buka setiap file di jendela Temukan Hasilkecuali<file migrasi time-stamp>_.cs di folder Migrasi , dengan mengklik dua kali satu baris untuk setiap file.
Buka dialog Ganti dalam File dan ubah Lihat keSemua Dokumen yang Terbuka.
Gunakan dialog Ganti dalam File untuk mengubah semua
InstructorID
menjadiPersonID.
Temukan semua file dalam proyek yang berisi
StudentID
.Buka setiap file di jendela Temukan Hasilkecuali<file migrasi time-stamp>_*.cs di folder Migrasi , dengan mengklik dua kali satu baris untuk setiap file.
Buka dialog Ganti dalam File dan ubah Lihat keSemua Dokumen yang Terbuka.
Gunakan dialog Ganti dalam File untuk mengubah semua
StudentID
menjadiPersonID
.Bangun proyek.
(Perhatikan bahwa ini menunjukkan kerugian pola classnameID
untuk penamaan kunci primer. Jika Anda telah menamai ID kunci primer tanpa awalan nama kelas, tidak ada penggantian nama yang diperlukan sekarang.)
Membuat dan Memperbarui File Migrasi
Di Package Manager Console (PMC), masukkan perintah berikut:
Add-Migration Inheritance
Jalankan Update-Database
perintah di PMC. Perintah akan gagal pada saat ini karena kami memiliki data yang ada yang tidak tahu cara menangani migrasi. Anda mendapatkan kesalahan berikut:
Pernyataan ALTER TABLE berkonflik dengan batasan FOREIGN KEY "FK_dbo. Department_dbo. Person_PersonID". Konflik terjadi dalam database "ContosoUniversity", tabel "dbo. Person", kolom 'PersonID'.
Buka Migrasi< >tanda waktu_Inheritance.cs dan ganti Up
metode dengan kode berikut:
public override void Up()
{
DropForeignKey("dbo.Department", "InstructorID", "dbo.Instructor");
DropForeignKey("dbo.OfficeAssignment", "InstructorID", "dbo.Instructor");
DropForeignKey("dbo.Enrollment", "StudentID", "dbo.Student");
DropForeignKey("dbo.CourseInstructor", "InstructorID", "dbo.Instructor");
DropIndex("dbo.Department", new[] { "InstructorID" });
DropIndex("dbo.OfficeAssignment", new[] { "InstructorID" });
DropIndex("dbo.Enrollment", new[] { "StudentID" });
DropIndex("dbo.CourseInstructor", new[] { "InstructorID" });
RenameColumn(table: "dbo.Department", name: "InstructorID", newName: "PersonID");
RenameColumn(table: "dbo.OfficeAssignment", name: "InstructorID", newName: "PersonID");
RenameColumn(table: "dbo.Enrollment", name: "StudentID", newName: "PersonID");
RenameColumn(table: "dbo.CourseInstructor", name: "InstructorID", newName: "PersonID");
CreateTable(
"dbo.Person",
c => new
{
PersonID = c.Int(nullable: false, identity: true),
LastName = c.String(maxLength: 50),
FirstName = c.String(maxLength: 50),
HireDate = c.DateTime(),
EnrollmentDate = c.DateTime(),
Discriminator = c.String(nullable: false, maxLength: 128),
OldId = c.Int(nullable: false)
})
.PrimaryKey(t => t.PersonID);
// Copy existing Student and Instructor data into new Person table.
Sql("INSERT INTO dbo.Person (LastName, FirstName, HireDate, EnrollmentDate, Discriminator, OldId) SELECT LastName, FirstName, null AS HireDate, EnrollmentDate, 'Student' AS Discriminator, StudentId AS OldId FROM dbo.Student");
Sql("INSERT INTO dbo.Person (LastName, FirstName, HireDate, EnrollmentDate, Discriminator, OldId) SELECT LastName, FirstName, HireDate, null AS EnrollmentDate, 'Instructor' AS Discriminator, InstructorId AS OldId FROM dbo.Instructor");
// Fix up existing relationships to match new PK's.
Sql("UPDATE dbo.Enrollment SET PersonId = (SELECT PersonId FROM dbo.Person WHERE OldId = Enrollment.PersonId AND Discriminator = 'Student')");
Sql("UPDATE dbo.Department SET PersonId = (SELECT PersonId FROM dbo.Person WHERE OldId = Department.PersonId AND Discriminator = 'Instructor')");
Sql("UPDATE dbo.OfficeAssignment SET PersonId = (SELECT PersonId FROM dbo.Person WHERE OldId = OfficeAssignment.PersonId AND Discriminator = 'Instructor')");
Sql("UPDATE dbo.CourseInstructor SET PersonId = (SELECT PersonId FROM dbo.Person WHERE OldId = CourseInstructor.PersonId AND Discriminator = 'Instructor')");
// Remove temporary key
DropColumn("dbo.Person", "OldId");
AddForeignKey("dbo.Department", "PersonID", "dbo.Person", "PersonID");
AddForeignKey("dbo.OfficeAssignment", "PersonID", "dbo.Person", "PersonID");
AddForeignKey("dbo.Enrollment", "PersonID", "dbo.Person", "PersonID", cascadeDelete: true);
AddForeignKey("dbo.CourseInstructor", "PersonID", "dbo.Person", "PersonID", cascadeDelete: true);
CreateIndex("dbo.Department", "PersonID");
CreateIndex("dbo.OfficeAssignment", "PersonID");
CreateIndex("dbo.Enrollment", "PersonID");
CreateIndex("dbo.CourseInstructor", "PersonID");
DropTable("dbo.Instructor");
DropTable("dbo.Student");
}
Jalankan lagi perintah 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 dapat melanjutkan tutorial dengan mengubah string koneksi dalam file Web.config atau menghapus database. Pendekatan paling sederhana adalah mengganti nama database dalam file Web.config . Misalnya, ubah nama database menjadi CU_test seperti yang diperlihatkan dalam contoh berikut:
<add name="SchoolContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=CU_Test;
Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\CU_Test.mdf"
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 Anda mengambil pendekatan ini untuk melanjutkan tutorial, lewati langkah penyebaran di akhir tutorial ini, karena situs yang disebarkan akan mendapatkan kesalahan yang sama ketika menjalankan migrasi secara otomatis. Jika Anda ingin memecahkan masalah kesalahan migrasi, sumber daya terbaik adalah salah satu forum Kerangka Kerja Entitas atau StackOverflow.com.
Pengujian
Jalankan situs dan coba berbagai halaman. Semuanya bekerja sama seperti sebelumnya.
Di Penjelajah Server, perluas SchoolContext lalu Tabel, dan Anda melihat bahwa tabel Pelajar dan Instruktur telah digantikan oleh tabel Orang . Perluas tabel Orang dan Anda melihat bahwa tabel tersebut memiliki semua kolom yang dulunya berada dalam tabel Siswa dan Instruktur .
Klik kanan tabel Orang, lalu klik Perlihatkan Data Tabel untuk melihat kolom diskriminator.
Diagram berikut mengilustrasikan struktur database Sekolah baru:
Ringkasan
Warisan tabel per hierarki sekarang telah diimplementasikan untuk Person
kelas , Student
, dan Instructor
. Untuk informasi selengkapnya tentang ini dan struktur warisan lainnya, lihat Strategi Pemetaan Warisan di blog Morteza Manavi. Dalam tutorial berikutnya Anda akan melihat beberapa cara untuk mengimplementasikan repositori dan unit pola kerja.
Tautan ke sumber daya Kerangka Kerja Entitas lainnya dapat ditemukan di Peta Konten Akses Data ASP.NET.
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