Condividi tramite


Esercitazione: Implementare l'ereditarietà con Entity Framework in un'app MVC 5 ASP.NET

Nell'esercitazione precedente sono state gestite le eccezioni di concorrenza. In questa esercitazione viene illustrato come implementare l'ereditarietà nel modello di dati.

Nella programmazione orientata agli oggetti è possibile usare l'ereditarietà per facilitare il riutilizzo del codice. In questa esercitazione verranno modificate le classi Instructor e Student in modo che derivino da una classe di base Person contenente proprietà quali LastName comuni a docenti e studenti. Non verranno aggiunte o modificate pagine Web, ma si modificherà parte del codice e le modifiche verranno automaticamente riflesse nel database.

In questa esercitazione:

  • Informazioni su come eseguire il mapping dell'ereditarietà al database
  • Creare la classe Person
  • Aggiornare Student e Instructor
  • Aggiungere person al modello
  • Creare e aggiornare migrazioni
  • Testare l'implementazione
  • Distribuisci in Azure

Prerequisiti

Eseguire il mapping dell'ereditarietà al database

Le Instructor classi e Student nel School modello di dati hanno diverse proprietà identiche:

Student_and_Instructor_classes

Si supponga di voler eliminare il codice ridondante per le proprietà condivise dalle entità Instructor e Student. Oppure che si desideri scrivere un servizio in grado di formattare i nomi senza sapere se il nome appartiene a un docente o a uno studente. È possibile creare una Person classe di base contenente solo le proprietà condivise, quindi impostare le Instructor entità e Student ereditano da tale classe di base, come illustrato nella figura seguente:

Student_and_Instructor_classes_deriving_from_Person_class

Questa struttura di ereditarietà può essere rappresentata nel database in diversi modi. È possibile avere una Person tabella che include informazioni su studenti e insegnanti in un'unica tabella. Alcune colonne possono essere applicate solo agli insegnanti (HireDate), alcuni solo agli studenti (EnrollmentDate), alcuni a entrambi (LastName, FirstName). In genere, è disponibile una colonna discriminatoria per indicare il tipo rappresentato da ogni riga. La colonna discriminante può ad esempio indicare "Instructor" per i docenti e "Student" per gli studenti.

Tabella per hierarchy_example

Questo modello di generazione di una struttura di ereditarietà delle entità da una singola tabella di database è detta ereditarietà di tabella per gerarchia (TPH).

Un'alternativa consiste nel rendere il database più simile alla struttura di ereditarietà. Ad esempio, è possibile avere solo i campi del nome nella Person tabella e avere tabelle separate Instructor e Student con i campi data.

Tabella per type_inheritance

Questo modello di creazione di una tabella di database per ogni classe di entità viene chiamato ereditarietà di tabella per tipo (TPT).

Un'altra opzione consiste nell'eseguire il mapping di tutti i tipi non astratti a singole tabelle. Tutte le proprietà di una classe, incluse le proprietà ereditate, eseguono il mapping alle colonne della tabella corrispondente. Questo criterio è denominato ereditarietà della classe tabella per tipo concreto. Se è stata implementata l'ereditarietà TPC per le Personclassi , Studente Instructor come illustrato in precedenza, le Student tabelle e Instructor non avranno un aspetto diverso dopo l'implementazione dell'ereditarietà rispetto a quelle precedenti.

I modelli di ereditarietà TPC e TPH offrono in genere prestazioni migliori in Entity Framework rispetto ai modelli di ereditarietà TPT, perché i modelli TPT possono comportare query di join complesse.

Questa esercitazione illustra come implementare l'ereditarietà tabella per gerarchia. TPH è il modello di ereditarietà predefinito in Entity Framework, quindi è necessario creare una Person classe, modificare le Instructor classi e Student per derivare da Person, aggiungere la nuova classe a DbContexte creare una migrazione. Per informazioni su come implementare gli altri modelli di ereditarietà, vedere Mapping dell'ereditarietà di tabella per tipo (TPT) e mapping dell'ereditarietà della classe Table-Per-Concrete (TPC) nella documentazione di MSDN Entity Framework.

Creare la classe Person

Nella cartella Models creare Person.cs e sostituire il codice del modello con il codice seguente:

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;
            }
        }
    }
}

Aggiornare Student e Instructor

Aggiornare ora Instructor.cs e Student.cs per ereditare i valori dal Person.sc.

In Instructor.cs derivare la Instructor classe dalla Person classe e rimuovere i campi chiave e nome. Il codice sarà simile all'esempio seguente:

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; }
    }
}

Apportare modifiche simili a Student.cs. La Student classe sarà simile all'esempio seguente:

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; }
    }
}

Aggiungere person al modello

In SchoolContext.cs aggiungere una DbSet proprietà per il Person tipo di entità:

public DbSet<Person> People { get; set; }

L'ereditarietà tabella per gerarchia in Entity Framework è stata configurata. Come si noterà, quando il database viene aggiornato, avrà una Person tabella al posto delle Student tabelle e Instructor .

Creare e aggiornare migrazioni

Nella console di Gestione pacchetti immettere il comando seguente:

Add-Migration Inheritance

Eseguire il Update-Database comando in PMC. Il comando avrà esito negativo a questo punto perché sono presenti dati esistenti che le migrazioni non sanno come gestire. Viene visualizzato un messaggio di errore simile al seguente:

Impossibile eliminare l'oggetto 'dbo. Instructor' perché fa riferimento a un vincolo FOREIGN KEY.

Aprire Migrazioni< timestamp>_Inheritance.cs e sostituire il Up metodo con il codice seguente:

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");
}

Questo codice esegue le attività di aggiornamento del database seguenti:

  • Rimuove i vincoli di chiave esterna e gli indici che puntano alla tabella Student.

  • Rinomina la tabella Instructor in Person e apporta le modifiche necessarie perché sia in grado di archiviare i dati Student:

    • Aggiunge un elemento EnrollmentDate che ammette i valori Null per gli studenti.
    • Aggiunge una colonna discriminante che indica se una riga è per uno studente o un docente.
    • Modifica HireDate in modo che ammetta i valori Null poiché le righe degli studenti non includono date di assunzione.
    • Aggiunge un campo temporaneo che sarà usato per aggiornare le chiavi esterne che puntano agli studenti. Quando si copiano gli studenti nella tabella Person, otterranno nuovi valori di chiave primaria.
  • Copia i dati dalla tabella Student alla tabella Person. Ciò comporta l'assegnazione di nuovi valori di chiave primaria agli studenti.

  • Corregge i valori di chiave esterna che puntano agli studenti.

  • Ricrea i vincoli di chiave esterna e gli indici che ora puntano alla tabella Person.

Se come tipo di chiave primaria fosse stato usato GUID anziché Integer, non sarebbe necessario modificare i valori di chiave primaria degli studenti e molti di questi passaggi avrebbero potuto essere omessi.

Eseguire di nuovo il comando update-database.

In un sistema di produzione è necessario apportare le modifiche corrispondenti al metodo Down nel caso in cui sia mai stato necessario usarlo per tornare alla versione precedente del database. Per questa esercitazione non si usa il metodo Down.

Nota

È possibile ottenere altri errori durante la migrazione dei dati e apportare modifiche allo schema. Se si verificano errori di migrazione non risolti, è possibile continuare con l'esercitazione modificando il stringa di connessione nel file diWeb.config o eliminando il database. L'approccio più semplice consiste nel rinominare il database nel file Web.config . Ad esempio, modificare il nome del database in ContosoUniversity2 come illustrato nell'esempio seguente:

<add name="SchoolContext" 
    connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=ContosoUniversity2;Integrated Security=SSPI;" 
    providerName="System.Data.SqlClient" />

Con un nuovo database, non sono presenti dati di cui eseguire la migrazione e il update-database comando è molto più probabile che venga completato senza errori. Per istruzioni su come eliminare il database, vedere Come eliminare un database da Visual Studio 2012. Se si usa questo approccio per continuare con l'esercitazione, ignorare il passaggio di distribuzione alla fine di questa esercitazione o distribuirlo in un nuovo sito e database. Se si distribuisce un aggiornamento nello stesso sito in cui è già stata eseguita la distribuzione, Ef riceverà lo stesso errore quando esegue automaticamente le migrazioni. Se si vuole risolvere un errore di migrazione, la risorsa migliore è uno dei forum di Entity Framework o StackOverflow.com.

Testare l'implementazione

Eseguire il sito e provare varie pagine. Tutto funziona come in precedenza.

In Esplora server espandere Connessioni dati\SchoolContext e quindi Tabelle e osservare che le tabelle Student e Instructor sono state sostituite da una tabella Person . Espandere la tabella Person e si noterà che contiene tutte le colonne usate per essere presenti nelle tabelle Student e Instructor .

Fare clic con il pulsante destro del mouse sulla tabella Person e quindi su Mostra dati tabella per vedere la colonna discriminante.

Il diagramma seguente illustra la struttura del nuovo database School:

School_database_diagram

Distribuisci in Azure

Questa sezione richiede di aver completato la sezione facoltativa Distribuzione dell'app in Azure nella parte 3, Ordinamento, filtro e paging di questa serie di esercitazioni. Se si sono verificati errori di migrazione risolti eliminando il database nel progetto locale, ignorare questo passaggio; oppure creare un nuovo sito e un nuovo database e distribuirlo nel nuovo ambiente.

  1. In Visual Studio fare clic con il pulsante destro del mouse sul progetto in Esplora soluzioni e scegliere Pubblica dal menu di scelta rapida.

  2. Fare clic su Pubblica.

    L'app Web viene aperta nel browser predefinito.

  3. Testare l'applicazione per verificare che funzioni.

    La prima volta che si esegue una pagina che accede al database, Entity Framework esegue tutti i metodi di migrazione Up necessari per aggiornare il database con il modello di dati corrente.

Ottenere il codice

Scaricare il progetto completato

Risorse aggiuntive

I collegamenti ad altre risorse di Entity Framework sono disponibili in ASP.NET Accesso ai dati - Risorse consigliate.

Per altre informazioni su questa e altre strutture di ereditarietà, vedere Modello di ereditarietà TPT e modello di ereditarietà TPH su MSDN. Nella prossima esercitazione si apprenderà come gestire diversi scenari Entity Framework relativamente avanzati.

Passaggi successivi

In questa esercitazione:

  • Si è appreso come eseguire il mapping dell'ereditarietà al database
  • Creare la classe Person
  • Aggiornare Student e Instructor
  • Aggiunta di Person al modello
  • Migrazioni create e aggiornate
  • Testare l'implementazione
  • Distribuito in Azure

Passare all'articolo successivo per informazioni sugli argomenti utili per essere consapevoli di quando si superano le nozioni di base dello sviluppo di ASP.NET applicazioni Web che usano Entity Framework Code First.