Öğretici: Devralma uygulama - ASP.NET MVC EF Core
Önceki öğreticide eşzamanlılık özel durumlarını işledik. Bu öğreticide, veri modelinde devralmanın nasıl uygulandığını gösterir.
Nesne odaklı programlamada, kodun yeniden kullanımını kolaylaştırmak için devralmayı kullanabilirsiniz. Bu öğreticide ve sınıflarını, hem eğitmenler hem de öğrenciler için ortak olan özellikler içeren bir temel sınıftan türetilen Instructor Student şekilde Person LastName değiştireceksiniz. Web sayfası eklemez veya değiştirmezsiniz, ancak kodun bir bölümü değişir ve bu değişiklikler veritabanına otomatik olarak yansıtılır.
Bu öğreticide şunları yaptınız:
- Devralmayı veritabanıyla eşleme
- Person sınıfını oluşturma
- Eğitmeni ve Öğrenciyi Güncelleştirme
- Modele Kişi Ekleme
- Geçişleri oluşturma ve güncelleştirme
- Uygulama test
Önkoşullar
Devralmayı veritabanıyla eşleme
School Instructor Student veri modelinde ve sınıflarında birbirinin aynı olan çeşitli özellikleri vardır:

ve varlıkları tarafından paylaşılan özellikler için yedekli kodu ortadan kaldırmak istediğinizi Instructor Student varsayalım. Veya adın bir eğitmenden mi yoksa öğrenciden mi geldiğine girmeden adları biçimlendiren bir hizmet yazmak da istemeyebilirsiniz. Aşağıdaki çizimde gösterildiği gibi, yalnızca bu paylaşılan özellikleri içeren bir temel sınıf oluşturabilir, ardından ve sınıflarını bu temel sınıftan Person Instructor Student devralmalarını sekleyebilirsiniz:

Bu devralma yapısının veritabanında temsili için çeşitli yollar vardır. Tek bir tabloda Person hem öğrenciler hem de eğitmenler hakkında bilgi içeren bir tablo olabilir. Sütunların bazıları yalnızca eğitmenler (HireDate), bazıları yalnızca öğrenciler (EnrollmentDate), bazıları da (LastName, FirstName) için geçerli olabilir. Genellikle her satırın hangi türü temsil ettiğini belirten bir ayrımcı sütuna sahip oluruz. Örneğin, ayrımcı sütunda eğitmenler için "Eğitmen" ve öğrenciler için "Öğrenci" olabilir.

Tek bir veritabanı tablosundan varlık devralma yapısı oluşturma deseni, hiyerarşi başına tablo (TPH) devralma olarak adlandırılan bir desendir.
Alternatif olarak, veritabanını devralma yapısına daha çok bensersiniz. Örneğin, tablodaki yalnızca ad alanlarına sahip ve tarih Person alanlarına sahip ayrı Instructor ve Student tablolara sahip olabilirsiniz.
Uyarı
Türe Göre Tablo (TPT) EF Core 3.x tarafından desteklanmaz, ancak 5.0'da EF Core uygulanmış.

Her varlık sınıfı için bir veritabanı tablosu oluşturma düzeni, türe göre tablo (TPT) devralma olarak adlandırılan bir desendir.
Bir diğer seçenek de soyut olmayan tüm türleri tek tek tablolarla eşlemektir. Devralınan özellikler de dahil olmak üzere bir sınıfın tüm özellikleri, karşılık gelen tablonun sütunlarıyla eşler. Bu desen, Somut Sınıf Başına Tablo (TPC) devralma olarak adlandırılan bir desendir. Daha önce gösterildiği gibi , ve sınıfları için TPC devralması yaptıysanız, ve tabloları devralmayı gerçekleştirdikten sonra öncekinden Person Student farklı Instructor Student Instructor olmaz.
TPT desenleri karmaşık birleştirme sorgularına neden olduğundan, TPC ve TPH devralma desenleri genellikle TPT devralma desenlerini daha iyi performans sunar.
Bu öğretici, TPH devralmanın nasıl uygulandığını gösterir. TPH, verinin desteklediği tek devralma Entity Framework Core düzenidir. Bir sınıf oluşturmak, ve sınıflarını'dan türetilen şekilde değiştirmek, yeni sınıfı 'a eklemek ve Person Instructor bir geçiş Student Person DbContext oluşturmaktır.
İpucu
Aşağıdaki değişiklikleri yapmadan önce projenin bir kopyasını kaydetmeyi göz önünde bulundurarak. Daha sonra, bu öğretici için yapılan adımları geri dönmek veya serinin başlangıcına dönmek yerine, sorunlarla karşı karşıdan geliyorsanız ve baştan başlamayı tercih ediyorsanız, kaydedilen projeden başlamak daha kolay olacaktır.
Person sınıfını oluşturma
Models klasöründe Person.cs oluşturun ve şablon kodunu aşağıdaki kodla değiştirin:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public abstract class Person
{
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; }
[Display(Name = "Full Name")]
public string FullName
{
get
{
return LastName + ", " + FirstMidName;
}
}
}
}
Eğitmeni ve Öğrenciyi Güncelleştirme
Eğitmen.cs içinde, Person sınıfından Eğitmen sınıfını türetin ve anahtar ve ad alanlarını kaldırın. Kod aşağıdaki örnekteki gibi olur:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public class Instructor : Person
{
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
[Display(Name = "Hire Date")]
public DateTime HireDate { get; set; }
public ICollection<CourseAssignment> CourseAssignments { get; set; }
public OfficeAssignment OfficeAssignment { get; set; }
}
}
Student.cs'de de aynı değişiklikleri yapın.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public class Student : Person
{
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
[Display(Name = "Enrollment Date")]
public DateTime EnrollmentDate { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
}
}
Modele Kişi Ekleme
Kişi varlık türünü SchoolContext.cs'ye ekleyin. Yeni satırlar vurgulanır.
using ContosoUniversity.Models;
using Microsoft.EntityFrameworkCore;
namespace ContosoUniversity.Data
{
public class SchoolContext : DbContext
{
public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
{
}
public DbSet<Course> Courses { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Student> Students { get; set; }
public DbSet<Department> Departments { get; set; }
public DbSet<Instructor> Instructors { get; set; }
public DbSet<OfficeAssignment> OfficeAssignments { get; set; }
public DbSet<CourseAssignment> CourseAssignments { get; set; }
public DbSet<Person> People { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Course>().ToTable("Course");
modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
modelBuilder.Entity<Student>().ToTable("Student");
modelBuilder.Entity<Department>().ToTable("Department");
modelBuilder.Entity<Instructor>().ToTable("Instructor");
modelBuilder.Entity<OfficeAssignment>().ToTable("OfficeAssignment");
modelBuilder.Entity<CourseAssignment>().ToTable("CourseAssignment");
modelBuilder.Entity<Person>().ToTable("Person");
modelBuilder.Entity<CourseAssignment>()
.HasKey(c => new { c.CourseID, c.InstructorID });
}
}
}
Hiyerarşi başına tablo devralmayı Entity Framework için tüm ihtiyaçlar bu kadardır. Gördüğünüz gibi veritabanı güncelleştirildiğinde Student ve Instructor tablolarının yerine Person tablosu yer alır.
Geçişleri oluşturma ve güncelleştirme
Değişikliklerinizi kaydedin ve projeyi derleme. Ardından proje klasöründeki komut penceresini açın ve aşağıdaki komutu girin:
dotnet ef migrations add Inheritance
Komutu henüz database update çalıştırabilirsiniz. Bu komut, Eğitmen tablosu bırakı ve Student tablosu olarak Person olarak yeniden adlandırılana kadar kayıp verilere neden olur. Mevcut verileri korumak için özel kod sağlamanız gerekir.
Migrations/ <timestamp> _Inheritance.cs'yi açın ve Up yöntemini aşağıdaki kodla değiştirin:
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Enrollment_Student_StudentID",
table: "Enrollment");
migrationBuilder.DropIndex(name: "IX_Enrollment_StudentID", table: "Enrollment");
migrationBuilder.RenameTable(name: "Instructor", newName: "Person");
migrationBuilder.AddColumn<DateTime>(name: "EnrollmentDate", table: "Person", nullable: true);
migrationBuilder.AddColumn<string>(name: "Discriminator", table: "Person", nullable: false, maxLength: 128, defaultValue: "Instructor");
migrationBuilder.AlterColumn<DateTime>(name: "HireDate", table: "Person", nullable: true);
migrationBuilder.AddColumn<int>(name: "OldId", table: "Person", nullable: true);
// Copy existing Student data into new Person table.
migrationBuilder.Sql("INSERT INTO dbo.Person (LastName, FirstName, HireDate, EnrollmentDate, Discriminator, OldId) SELECT LastName, FirstName, null AS HireDate, EnrollmentDate, 'Student' AS Discriminator, ID AS OldId FROM dbo.Student");
// Fix up existing relationships to match new PK's.
migrationBuilder.Sql("UPDATE dbo.Enrollment SET StudentId = (SELECT ID FROM dbo.Person WHERE OldId = Enrollment.StudentId AND Discriminator = 'Student')");
// Remove temporary key
migrationBuilder.DropColumn(name: "OldID", table: "Person");
migrationBuilder.DropTable(
name: "Student");
migrationBuilder.CreateIndex(
name: "IX_Enrollment_StudentID",
table: "Enrollment",
column: "StudentID");
migrationBuilder.AddForeignKey(
name: "FK_Enrollment_Person_StudentID",
table: "Enrollment",
column: "StudentID",
principalTable: "Person",
principalColumn: "ID",
onDelete: ReferentialAction.Cascade);
}
Bu kod aşağıdaki veritabanı güncelleştirme görevleriyle ilgilenmektedir:
Student tablosuna işaret etmek için yabancı anahtar kısıtlamalarını ve dizinlerini kaldırır.
Eğitmen tablosu kişi olarak yeniden adlandırılır ve Öğrenci verilerini depolamak için gerekli değişiklikleri yapar:
Öğrenciler için null değere sahip enrollmentDate ekler.
Satırın öğrenci mi yoksa eğitmen mi olduğunu belirtmek için Ayrımcı sütun ekler.
Öğrenci satırları işe alma tarihlerine sahip olmayacaktır, bu durumda HireDate null değere değiştirilebilir.
Öğrencileri işaret edecek yabancı anahtarları güncelleştirmek için kullanılacak geçici bir alan ekler. Öğrencileri Kişi tablosuna kopyalayıp yeni birincil anahtar değerlerini alırlar.
Student tablosundan Person tablosuna veri kopyalar. Bu, öğrencilerin yeni birincil anahtar değerleri atamalarını sağlar.
Öğrencileri işaret eden yabancı anahtar değerlerini düzeltir.
Yabancı anahtar kısıtlamalarını ve dizinlerini yeniden oluşturur ve bunları Person tablosuna işaret ediyor.
(Birincil anahtar türü olarak tamsayı yerine GUID kullandıysanız, öğrenci birincil anahtar değerlerinin değişmesi gerekmeyecek ve bu adımlardan birkaçı atlanmış olabilir.)
Şu komutu database update çalıştırın:
dotnet ef database update
(Bir üretim sisteminde, önceki veritabanı sürümüne geri dönmek için bunu kullanmak zorunda olmak durumunda Down yönteminde ilgili değişiklikler yapabilirsiniz. Bu öğreticide yöntemini Down kullanmazsiniz.)
Not
Mevcut verileri olan bir veritabanında şema değişiklikleri yaparken başka hatalar da almak mümkündür. Çözümleyemeyecek geçiş hataları alırsanız, bağlantı dizesinde veritabanı adını değiştirebilir veya veritabanını silebilirsiniz. Yeni bir veritabanıyla geçirilir veri yoktur ve update-database komutunun hatasız tamamlanması daha olasıdır. Veritabanını silmek için SSOX kullanın veya database drop CLI komutunu çalıştırın.
Uygulama test
Uygulamayı çalıştırın ve çeşitli sayfaları deneyin. Her şey daha önce olduğu gibi çalışır.
Bu SQL Server Nesne Gezgini Veri Bağlantıları/SchoolContext ve ardından Tablolar'ı genişletin. Öğrenci ve Eğitmen tablolarının yerini Kişi tablosu alırsınız. Kişi tablosu tasarımcısını açın ve bunun, Eskiden Öğrenci ve Eğitmen tablolarında yer alan tüm sütunların olduğunu görüyorsunuz.

Kişi tablosuna sağ tıklayın ve ardından Ayrımcı sütunu görmek için Tablo Verilerini Göster'e tıklayın.

Kodu alma
Tamamlanan uygulamayı indirme veya görüntüleme.
Ek kaynaklar
Örneğinde devralma hakkında daha fazla bilgi Entity Framework Core bkz. Devralma.
Sonraki adımlar
Bu öğreticide şunları yaptınız:
- Veritabanına eşlenmiş devralma
- Person sınıfı oluşturuldu
- Güncelleştirilmiş Eğitmen ve Öğrenci
- Modele Kişi eklendi
- Geçişler oluşturuldu ve güncelleştirildi
- Uygulama test edildi
Çeşitli görece gelişmiş örnek senaryolarını işlemeyi öğrenmek için sonraki öğreticiye Entity Framework ilerleyin.