Kurz: Implementace dědičnosti pomocí EF v aplikaci ASP.NET MVC 5
V předchozím kurzu jste zpracovávali výjimky souběžnosti. V tomto kurzu se dozvíte, jak implementovat dědičnost v datovém modelu.
V objektově orientovaném programování můžete k usnadnění opakovaného použití kódu použít dědičnost. V tomto kurzu změníte Instructor
třídy a Student
tak, aby byly odvozené ze Person
základní třídy, která obsahuje vlastnosti, jako LastName
jsou společné pro instruktory i studenty. Nebudete přidávat ani měnit žá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:
- Naučte se mapovat dědičnost k databázi.
- Vytvoření třídy Person
- Aktualizace instruktora a studenta
- Přidání osoby do modelu
- Vytváření a aktualizace migrací
- Testování implementace
- Nasazení do Azure
Požadavky
Mapování dědičnosti na databázi
Třídy Instructor
a Student
v datovém School
modelu mají několik identických vlastností:
Předpokládejme, že chcete eliminovat redundantní kód pro vlastnosti, které jsou sdíleny entitami Instructor
a Student
. Nebo chcete napsat službu, která může formátovat jména bez ohledu na to, jestli jméno pochází od instruktora nebo studenta. Mohli byste vytvořit Person
základní třídu, která obsahuje pouze tyto sdílené vlastnosti, a pak z této základní třídy zdědit Instructor
entity a Student
, jak je znázorněno na následujícím obrázku:
Existuje několik způsobů, jak by tato struktura dědičnosti mohla být v databázi reprezentována. Můžete mít Person
tabulku, která obsahuje informace o studentech i vyučujícím v jedné tabulce. Některé sloupce by se mohly vztahovat pouze na instruktory (HireDate
), některé pouze na studenty (EnrollmentDate
), některé pro oba (LastName
, FirstName
). Obvykle byste měli diskriminující sloupec, který označuje typ, který jednotlivé řádky představují. Například sloupec diskriminátoru může obsahovat "Instruktor" pro instruktory a "Student" pro studenty.
Tento model generování struktury dědičnosti entit z jedné databázové tabulky se nazývá dědičnost TPH (table-per-hierarchy ).
Alternativou je, aby databáze vypadala více jako struktura dědičnosti. Můžete například mít v Person
tabulce jenom pole názvů a samostatné Instructor
tabulky a Student
tabulky s poli kalendářního data.
Tento model vytvoření databázové tabulky pro každou třídu entit se nazývá dědičnost typu (TPT ).
Další možností je namapovat všechny ne abstraktní typy na jednotlivé tabulky. Všechny vlastnosti třídy, včetně zděděných vlastností, se mapují na sloupce odpovídající tabulky. Tento vzor se nazývá dědičnost třídy TPC (Table-per-Concrete Class). Pokud jste implementovali dědičnost TPC pro Person
třídy , Student
a Instructor
, jak je znázorněno výše, Student
tabulky a Instructor
by po implementaci dědičnosti vypadaly stejně jako předtím.
Vzory dědičnosti TPC a TPH obecně poskytují v Entity Frameworku lepší výkon než vzory dědičnosti TPT, protože vzory TPT mohou vést ke složitým dotazům spojení.
Tento kurz ukazuje, jak implementovat dědičnost TPH. TPH je výchozí vzor dědičnosti v Entity Frameworku, takže vše, co musíte udělat, je vytvořit Person
třídu, změnit Instructor
třídy a Student
tak, aby byly odvozeny z Person
, přidat novou třídu do objektu DbContext
a vytvořit migraci. (Informace o tom, jak implementovat ostatní vzory dědičnosti, najdete v tématech Mapování dědičnosti tpt (Table-Per-Type) a Mapování dědičnosti třídy TPC (Table-Per-Concrete) v dokumentaci msdn Entity Framework.)
Vytvoření třídy Person
Ve složce Models vytvořte soubor 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
Teď aktualizujte soubory Instructor.cs a Student.cs tak, aby dědily hodnoty z Person.sc.
V souboru Instructor.cs odvozujte Instructor
třídu z Person
třídy a odeberte pole klíče a názvu. 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 virtual ICollection<Course> Courses { get; set; }
public virtual OfficeAssignment OfficeAssignment { get; set; }
}
}
Proveďte podobné změny v souboru Student.cs. Třída Student
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 Student : Person
{
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
[Display(Name = "Enrollment Date")]
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
}
Přidání osoby do modelu
V souboru SchoolContext.cs přidejte DbSet
vlastnost pro Person
typ entity:
public DbSet<Person> People { get; set; }
To je vše, co Entity Framework potřebuje ke konfiguraci dědičnosti tabulek na hierarchii. Jak uvidíte, když se databáze aktualizuje, bude mít Person
místo Student
tabulek a Instructor
tabulku.
Vytváření a aktualizace migrací
V konzole Správce balíčků (PMC) zadejte následující příkaz:
Add-Migration Inheritance
Spusťte příkaz Update-Database
v PMC. Příkaz v tomto okamžiku selže, protože máme existující data, která migrace nevědí, jak je zpracovat. Zobrazí se podobná chybová zpráva:
Objekt dbo nelze odstranit. Instruktor' protože na něj odkazuje omezení CIZÍHO KLÍČE.
Otevřené migrace< timestamp>_Inheritance.cs a nahraďte metodu Up
následujícím kódem:
public override void Up()
{
// Drop foreign keys and indexes that point to tables we're going to drop.
DropForeignKey("dbo.Enrollment", "StudentID", "dbo.Student");
DropIndex("dbo.Enrollment", new[] { "StudentID" });
RenameTable(name: "dbo.Instructor", newName: "Person");
AddColumn("dbo.Person", "EnrollmentDate", c => c.DateTime());
AddColumn("dbo.Person", "Discriminator", c => c.String(nullable: false, maxLength: 128, defaultValue: "Instructor"));
AlterColumn("dbo.Person", "HireDate", c => c.DateTime());
AddColumn("dbo.Person", "OldId", c => c.Int(nullable: true));
// Copy existing Student 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, ID AS OldId FROM dbo.Student");
// Fix up existing relationships to match new PK's.
Sql("UPDATE dbo.Enrollment SET StudentId = (SELECT ID FROM dbo.Person WHERE OldId = Enrollment.StudentId AND Discriminator = 'Student')");
// Remove temporary key
DropColumn("dbo.Person", "OldId");
DropTable("dbo.Student");
// Re-create foreign keys and indexes pointing to new table.
AddForeignKey("dbo.Enrollment", "StudentID", "dbo.Person", "ID", cascadeDelete: true);
CreateIndex("dbo.Enrollment", "StudentID");
}
Tento kód se postará o následující úlohy aktualizace databáze:
Odebere omezení cizího klíče a indexy, které odkazují na tabulku Student.
Přejmenuje tabulku Instruktor na Person (Osoba) a provede změny potřebné k ukládání dat studentů:
- Přidá hodnotu EnrollmentDate s možnou hodnotou null pro studenty.
- Přidá sloupec Discriminator určující, jestli je řádek určený pro studenta nebo instruktora.
- Nastaví hodnotu HireDate s možnou hodnotou null, protože řádky studentů nebudou mít data přijetí.
- Přidá dočasné pole, které se použije k aktualizaci cizích klíčů, které odkazují na studenty. Když zkopírujete studenty do tabulky Osoba, získají nové hodnoty primárního klíče.
Zkopíruje data z tabulky Student do tabulky Osoba. To způsobí, že studenti dostanou přiřazené nové hodnoty primárního klíče.
Opravuje hodnoty cizího klíče, které odkazují na studenty.
Znovu vytvoří omezení cizího klíče a indexy a teď je nasměruje na tabulku Osoba.
(Pokud byste jako typ primárního klíče použili identifikátor GUID místo celého čísla, hodnoty primárního klíče studenta by se nemusely měnit a některé z těchto kroků mohly být vynechány.)
Spusťte příkaz update-database
znovu.
(V produkčním systému byste provedli odpovídající změny v metodě Down, pokud byste ji někdy museli použít, abyste se vrátili k předchozí verzi databáze. Pro účely tohoto kurzu nebudete používat metodu Down.)
Poznámka
Při migraci dat a provádění změn schématu se můžou zobrazit další chyby. Pokud dojde k chybám migrace, které nemůžete vyřešit, můžete pokračovat v kurzu změnou připojovací řetězec v souboru Web.config nebo odstraněním databáze. Nejjednodušším způsobem je přejmenovat databázi v souboruWeb.config . Například změňte název databáze na ContosoUniversity2, jak je znázorněno v následujícím příkladu:
<add name="SchoolContext"
connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=ContosoUniversity2;Integrated Security=SSPI;"
providerName="System.Data.SqlClient" />
U nové databáze nejsou k dispozici žádná data, která by bylo možné migrovat, a update-database
je mnohem pravděpodobnější, že se příkaz dokončí bez chyb. Pokyny k odstranění databáze najdete v tématu Jak odstranit databázi ze sady Visual Studio 2012. Pokud tento přístup zvolíte, abyste mohli pokračovat v kurzu, přeskočte krok nasazení na konci tohoto kurzu nebo nasaďte do nové lokality a databáze. Pokud nasadíte aktualizaci na stejnou lokalitu, na kterou jste už nasadili, ef tam při automatickém spuštění migrací zobrazí stejnou chybu. Pokud chcete vyřešit chybu migrace, je nejlepším prostředkem jedno z fór nebo StackOverflow.com Entity Frameworku.
Testování implementace
Spusťte web a vyzkoušejte různé stránky. Všechno funguje stejně jako předtím.
V Průzkumníku serveru rozbalte Data Connections\SchoolContext a pak Tabulky a uvidíte, že tabulky Student a Instructor byly nahrazeny tabulkou Person . Rozbalte tabulku Osoba a uvidíte, že obsahuje všechny sloupce, které bývaly v tabulkách Student a Instruktor .
Klikněte pravým tlačítkem na tabulku Person (Osoba) a potom klikněte na Show Table Data (Zobrazit data tabulky ), aby se zobrazil diskriminující sloupec.
Následující diagram znázorňuje strukturu nové školní databáze:
Nasazení do Azure
Tato část vyžaduje, abyste dokončili volitelnou část Nasazení aplikace do Azure v části 3, Řazení, filtrování a stránkování této série kurzů. Pokud došlo k chybám migrace, které jste vyřešili odstraněním databáze v místním projektu, přeskočte tento krok. nebo vytvořte novou lokalitu a databázi a nasaďte je do nového prostředí.
V sadě Visual Studio klikněte pravým tlačítkem na projekt v Průzkumník řešení a v místní nabídce vyberte Publikovat.
Klikněte na Publikovat.
Webová aplikace se otevře ve výchozím prohlížeči.
Otestujte aplikaci a ověřte, že funguje.
Při prvním spuštění stránky, která přistupuje k databázi, Entity Framework spustí všechny metody migrace
Up
potřebné k tomu, aby databáze byla aktuální s aktuálním datovým modelem.
Získání kódu
Další materiály
Odkazy na další prostředky Entity Frameworku najdete v tématu ASP.NET Přístup k datům – doporučené zdroje.
Další informace o této a dalších strukturách dědičnosti najdete v tématu Model dědičnosti TPT a vzor dědičnosti TPH na webu MSDN. V dalším kurzu se dozvíte, jak zpracovat celou řadu relativně pokročilých scénářů Entity Frameworku.
Další kroky
V tomto kurzu jste:
- Naučili jsme se mapovat dědičnost na databázi.
- Vytvoření třídy Person
- Aktualizovaný instruktor a student
- Přidání osoby do modelu
- Vytvoření a aktualizace migrací
- Otestovali implementaci.
- Nasazeno do Azure
V dalším článku se dozvíte o tématech, která jsou užitečná, když přejdete nad rámec základů vývoje ASP.NET webových aplikací, které používají Entity Framework Code First.