kurz: implementace dědění – ASP.NET MVC s EF Core
V předchozím kurzu jste zpracovali výjimky souběžnosti. Tento kurz vám ukáže, jak implementovat dědičnost v datovém modelu.
V objektově orientovaném programování můžete použít dědičnost k usnadnění opětovného použití kódu. V tomto kurzu změníte Instructor Student třídy a tak, aby byly odvozeny ze Person základní třídy, která obsahuje vlastnosti LastName , jako jsou společné pro instruktory i studenty. Nepřidáte ani neměníte žádné webové stránky, ale změníte část kódu a tyto změny se automaticky projeví v databázi.
V tomto kurzu jste:
- Mapování dědičnosti na databázi
- Vytvoření třídy Person
- Aktualizace instruktora a studenta
- Přidat osobu do modelu
- Vytváření a aktualizace migrací
- Testování implementace
Požadavky
Mapování dědičnosti na databázi
InstructorTřídy a Student v modelu školních dat obsahují několik vlastností, které jsou identické:

Předpokládejme, že chcete eliminovat redundantní kód pro vlastnosti, které jsou sdíleny Instructor Student entitami a. Nebo chcete napsat službu, která může formátovat názvy bez caring, jestli název pochází od instruktora nebo studenta. Můžete vytvořit Person základní třídu, která obsahuje pouze tyto sdílené vlastnosti, a poté nastavit Instructor třídy a Student dědění z této základní třídy, jak je znázorněno na následujícím obrázku:

Existuje několik způsobů, jak lze tuto strukturu dědičnosti znázornit v databázi. Můžete mít Person tabulku, která obsahuje informace o studentech i instruktorech v jedné tabulce. Některé sloupce mohou být použity pouze pro instruktory (ZaměstnánOd), některé pouze pro studenty (EnrollmentDate), některá pro obě (LastName, FirstName). Obvykle byste měli sloupec diskriminátoru, který určuje, který typ každý řádek představuje. Sloupec diskriminátoru může mít například "instruktor" pro studenty a studenta.

Tento model generování struktury dědičnosti entit z jedné databázové tabulky se nazývá dědičnost typu tabulka-na hierarchii (TPH) .
Alternativou je, že databáze vypadá podobně jako struktura dědičnosti. Například můžete mít v tabulce pouze pole s názvem Person , která mají samostatné Instructor a Student tabulky s poli data.
Upozornění
V EF Core 3. x se nepodporuje tabulka na typ (TPT), ale je ale implementovaná v EF Core 5,0.

Tento model vytvoření tabulky databáze pro každou třídu entity se nazývá dědění typu Table-per-Type (TPT) .
Ještě další možností je mapovat všechny neabstraktní typy na jednotlivé tabulky. Všechny vlastnosti třídy, včetně děděných vlastností, jsou mapovány na sloupce odpovídající tabulky. Tento model se nazývá dědičnost tříd (TPC) podle konkrétní třídy . Pokud jste implementovali dědičnost TPC pro Person Student třídy, a, jak je Instructor uvedeno výše, Student Instructor tabulky a by po implementaci dědičnosti nevypadaly jinak než předtím.
Vzorce dědičnosti TPC a TPH obvykle poskytují lepší výkon než vzory dědičnosti TPT, protože vzory TPT mohou mít za následek složité spojení dotazů.
Tento kurz ukazuje, jak implementovat dědičnosti TPH. TPH je jediný vzorek dědičnosti, který Entity Framework Core podporuje. To, co uděláte, je vytvořit Person třídu, změnit Instructor třídy a, Student které se mají odvodit z Person , přidat novou třídu do a DbContext vytvořit migraci.
Tip
Zvažte uložení kopie projektu před provedením následujících změn. Pak Pokud narazíte na problémy a potřebujete začít znovu, bude snazší začít z uloženého projektu místo vrácení kroků provedených pro tento kurz nebo přechod zpět na začátek celé řady.
Vytvoření třídy Person
Ve složce modely vytvořte Person. cs a nahraďte kód šablony následujícím kódem:
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;
}
}
}
}
Aktualizace instruktora a studenta
V instruktor. cs odvodíte třídu Instructor z třídy Person a odeberte pole klíč a název. Kód bude vypadat jako v následujícím příkladu:
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; }
}
}
Proveďte stejné změny v student. cs.
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; }
}
}
Přidat osobu do modelu
Do SchoolContext. cs přidejte typ entity Person. Nové řádky jsou zvýrazněny.
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 });
}
}
}
To je vše, co Entity Framework potřebuje, aby bylo možné nakonfigurovat dědičnost tabulek na hierarchii. Jak vidíte, při aktualizaci databáze bude mít uživatelskou tabulku místo tabulek student a instruktor.
Vytváření a aktualizace migrací
Uložte změny a sestavte projekt. Pak otevřete okno příkazového řádku ve složce projektu a zadejte následující příkaz:
dotnet ef migrations add Inheritance
Tento database update příkaz ještě nespouštějte. Tento příkaz bude mít za následek ztrátu dat, protože odstraní tabulku instruktora a přejmenuje tabulku student na Person. Aby bylo možné zachovat existující data, je třeba zadat vlastní kód.
Otevřete migrace/ <timestamp> _Inheritance. cs a nahraďte Up metodu následujícím kódem:
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);
}
Tento kód má na starosti následující úlohy aktualizace databáze:
Odebere omezení a indexy cizího klíče, které odkazují na tabulku studenta.
Přejmenuje tabulku instruktora jako osobu a provede změny potřebné k uložení dat studenta:
Přidá EnrollmentDate s možnou hodnotou null pro studenty.
Přidá sloupec diskriminátoru, který označuje, zda je řádek určen studentem nebo instruktorem.
Vytvoří hodnotu Nullable s možnou hodnotou null, protože řádky studenta nebudou mít data přijetí.
Přidá dočasné pole, které bude použito k aktualizaci cizích klíčů, které odkazují na studenty. Když zkopírujete studenty do tabulky Person, získají se nové hodnoty primárního klíče.
Zkopíruje data z tabulky student do tabulky Person (osoba). To způsobí, že studenti získají přiřazené nové hodnoty primárního klíče.
Opravuje hodnoty cizích klíčů, které odkazují na studenty.
Znovu vytvoří omezení a indexy cizího klíče, které se teď odkazují na tabulku Person.
(Pokud jste použili GUID místo celého čísla jako typ primárního klíče, hodnoty primárního klíče studenta se nemusejí změnit a některé z těchto kroků by mohly být vynechány.)
Spusťte database update příkaz:
dotnet ef database update
(V produkčním systému provedete odpovídající změny Down metody v případě, že byste to museli použít pro návrat k předchozí verzi databáze. Pro tento kurz nebudete používat Down metodu.)
Poznámka
Při provádění změn schématu v databázi, která obsahuje existující data, je možné získat další chyby. Pokud získáte chyby migrace, které nelze vyřešit, můžete buď změnit název databáze v připojovacím řetězci nebo odstranit databázi. V případě nové databáze není k dispozici žádná data k migraci a příkaz Update-Database je pravděpodobnější, že se dokončí bez chyb. Databázi odstraníte tak, že použijete SSOX nebo spustíte database drop příkaz CLI.
Testování implementace
Spusťte aplikaci a vyzkoušejte si různé stránky. Vše funguje stejně jako dříve.
v SQL Server Průzkumník objektů rozbalte Data připojení/SchoolContext a pak tabulky a uvidíte, že tabulky Student a instruktor byly nahrazeny tabulkou Person. Otevřete návrháře tabulky osoba a uvidíte, že obsahuje všechny sloupce, které se používají v tabulkách student a instruktor.

Klikněte pravým tlačítkem myši na tabulku Person a potom kliknutím na možnost Zobrazit data tabulky zobrazte sloupec diskriminátor.

Získání kódu
Stažení nebo zobrazení dokončené aplikace.
Další zdroje informací
Další informace o dědičnosti v Entity Framework Core naleznete v tématu Dědičnost.
Další kroky
V tomto kurzu jste:
- Namapovaná dědičnost na databázi
- Byla vytvořena třída Person.
- Aktualizovaný instruktor a student
- Přidání osob do modelu
- Vytvoření a aktualizace migrací
- Otestovali jste implementaci.
V dalším kurzu se dozvíte, jak zvládnout celou řadu relativně pokročilých scénářů Entity Framework scénářů.