Sdílet prostřednictvím


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í:

Student_and_Instructor_classes

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:

Student_and_Instructor_classes_deriving_from_Person_class

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.

Tabulka na hierarchy_example

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.

Tabulka na type_inheritance

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 Persontřídy , Studenta 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 DbContexta 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:

School_database_diagram

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

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

  2. Klikněte na Publikovat.

    Webová aplikace se otevře ve výchozím prohlížeči.

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

Stáhnout dokončený projekt

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.