Udostępnij za pośrednictwem


Samouczek: implementowanie dziedziczenia za pomocą platformy EF w aplikacji MVC 5 ASP.NET

W poprzednim samouczku obsłużyliśmy wyjątki współbieżności. W tym samouczku pokazano, jak zaimplementować dziedziczenie w modelu danych.

W programowaniu obiektowym można użyć dziedziczenia w celu ułatwienia ponownego użycia kodu. W tym samouczku zmienisz Instructor klasy i Student tak, aby pochodziły one z klasy bazowej Person , która zawiera właściwości, takie jak LastName wspólne dla instruktorów i studentów. Nie dodasz ani nie zmienisz żadnych stron sieci Web, ale zmienisz część kodu, a te zmiany zostaną automatycznie odzwierciedlone w bazie danych.

W tym samouczku zostały wykonane następujące czynności:

  • Dowiedz się, jak mapować dziedziczenie do bazy danych
  • Tworzenie klasy Person
  • Aktualizowanie instruktora i ucznia
  • Dodawanie osoby do modelu
  • Tworzenie i aktualizowanie migracji
  • Testowanie implementacji
  • Wdróż na platformie Azure

Wymagania wstępne

Mapuj dziedziczenie na bazę danych

Klasy Instructor i Student w School modelu danych mają kilka właściwości, które są identyczne:

Student_and_Instructor_classes

Załóżmy, że chcesz wyeliminować nadmiarowy kod dla właściwości udostępnianych przez Instructor jednostki i Student . Możesz też napisać usługę, która może formatować nazwy bez dbania o to, czy nazwa pochodzi od instruktora, czy studenta. Można utworzyć klasę bazową zawierającą Person tylko te właściwości udostępnione, a następnie sprawić, aby Instructor jednostki i Student dziedziczyły po tej klasie bazowej, jak pokazano na poniższej ilustracji:

Student_and_Instructor_classes_deriving_from_Person_class

Istnieje kilka sposobów reprezentacji tej struktury dziedziczenia w bazie danych. Możesz mieć tabelę zawierającą Person informacje o uczniach i instruktorach w jednej tabeli. Niektóre kolumny mogą być stosowane tylko do instruktorów (HireDate), niektóre tylko dla uczniów (EnrollmentDate), niektóre do obu (LastName, FirstName). Zazwyczaj istnieje kolumna dyskryminująca wskazująca typ reprezentowany przez każdy wiersz. Na przykład kolumna dyskryminująca może mieć wartość "Instruktor" dla instruktorów i "Student" dla uczniów.

Tabela na hierarchy_example

Ten wzorzec generowania struktury dziedziczenia jednostki z pojedynczej tabeli bazy danych jest nazywany dziedziczeniem TPH ( table-per-hierarchy ).

Alternatywą jest uczynienie bazy danych bardziej podobną do struktury dziedziczenia. Na przykład można mieć tylko pola nazw w Person tabeli i mieć oddzielne Instructor tabele z Student polami daty.

Tabela na type_inheritance

Ten wzorzec tworzenia tabeli bazy danych dla każdej klasy jednostki jest nazywany dziedziczeniem typu (TPT).

Kolejną opcją jest mapowania wszystkich typów nie abstrakcyjnych na poszczególne tabele. Wszystkie właściwości klasy, w tym właściwości dziedziczone, są mapowane na kolumny odpowiedniej tabeli. Ten wzorzec jest nazywany dziedziczeniem klasy tabela-betonowej (TPC). Jeśli zaimplementowano dziedziczenie TPC dla Personklas , Studenti Instructor , jak pokazano wcześniej, Student tabele i Instructor nie będą wyglądać inaczej po zaimplementowaniu dziedziczenia niż wcześniej.

Wzorce dziedziczenia TPC i TPH zwykle zapewniają lepszą wydajność w programie Entity Framework niż wzorce dziedziczenia TPT, ponieważ wzorce TPT mogą powodować złożone zapytania sprzężenia.

W tym samouczku pokazano, jak zaimplementować dziedziczenie TPH. TPH jest domyślnym wzorcem dziedziczenia w programie Entity Framework, więc wystarczy utworzyć klasę Person , zmienić Instructor klasy i Student , aby pochodzą z Personklasy , dodać nową klasę do DbContextklasy i i utworzyć migrację. (Aby uzyskać informacje na temat implementowania innych wzorców dziedziczenia, zobacz Mapowanie dziedziczenia tabeli na typ (TPT) i mapowanie dziedziczenia klasy tabela-per-beton (TPC) w dokumentacji programu MSDN Entity Framework.

Tworzenie klasy Person

W folderze Models utwórz plik Person.cs i zastąp kod szablonu następującym kodem:

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

Aktualizowanie instruktora i ucznia

Teraz zaktualizuj pliki Instructor.cs i Student.cs , aby dziedziczyły wartości z Person.sc.

W pliku Instructor.cs utwórz klasę Instructor z Person klasy i usuń pola klucza i nazwy. Kod będzie wyglądać podobnie do następującego przykładu:

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

Wprowadź podobne zmiany w pliku Student.cs. Klasa Student będzie wyglądać podobnie do następującego przykładu:

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

Dodawanie osoby do modelu

W pliku SchoolContext.cs dodaj DbSet właściwość dla Person typu jednostki:

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

Jest to wszystko, którego potrzebuje program Entity Framework w celu skonfigurowania dziedziczenia tabeli na hierarchię. Jak zobaczysz, po zaktualizowaniu bazy danych będzie ona zawierać tabelę Person zamiast Student tabel i Instructor .

Tworzenie i aktualizowanie migracji

W konsoli menedżera pakietów (PMC) wprowadź następujące polecenie:

Add-Migration Inheritance

Update-Database Uruchom polecenie w usłudze PMC. Polecenie zakończy się niepowodzeniem w tym momencie, ponieważ mamy istniejące dane, które migracje nie wiedzą, jak je obsługiwać. Zostanie wyświetlony komunikat o błędzie podobny do następującego:

Nie można usunąć obiektu "dbo". Instruktor, ponieważ jest przywoływane przez ograniczenie KLUCZA OBCEGO.

Open Migrations< timestamp>_Inheritance.cs i zastąp metodę Up następującym kodem:

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

Ten kod zajmuje się następującymi zadaniami aktualizacji bazy danych:

  • Usuwa ograniczenia klucza obcego i indeksy wskazujące tabelę Student.

  • Zmienia nazwę tabeli Instruktor jako Osoba i wprowadza zmiany wymagane do przechowywania danych uczniów:

    • Dodaje wartość null EnrollmentDate dla uczniów.
    • Dodaje kolumnę dyskryminującą, aby wskazać, czy wiersz jest przeznaczony dla ucznia, czy instruktora.
    • Sprawia, że funkcja HireDate może mieć wartość null, ponieważ wiersze uczniów nie będą miały dat zatrudnienia.
    • Dodaje pole tymczasowe, które będzie używane do aktualizowania kluczy obcych wskazujących uczniów. Podczas kopiowania uczniów do tabeli Person otrzymają nowe wartości klucza podstawowego.
  • Kopiuje dane z tabeli Student do tabeli Person. Powoduje to przypisanie nowych wartości klucza podstawowego przez uczniów.

  • Naprawia obce wartości kluczy wskazujące uczniów.

  • Ponownie tworzy ograniczenia i indeksy kluczy obcych, a teraz wskazują je na tabelę Person.

(Jeśli użyto identyfikatora GUID zamiast liczby całkowitej jako typu klucza podstawowego, wartości klucza podstawowego ucznia nie musiałyby ulec zmianie, a kilka z tych kroków mogło zostać pominiętych).

Uruchom ponownie polecenie update-database.

(W systemie produkcyjnym należy wprowadzić odpowiednie zmiany w metodzie Down, jeśli kiedykolwiek trzeba było jej użyć, aby wrócić do poprzedniej wersji bazy danych. W tym samouczku nie będziesz używać metody Down.

Uwaga

Podczas migrowania danych i wprowadzania zmian schematu można uzyskać inne błędy. Jeśli wystąpią błędy migracji, których nie możesz rozwiązać, możesz kontynuować pracę z samouczkiem, zmieniając parametry połączenia w pliku Web.config lub usuwając bazę danych. Najprostszym podejściem jest zmiana nazwy bazy danych w pliku Web.config . Na przykład zmień nazwę bazy danych na ContosoUniversity2, jak pokazano w poniższym przykładzie:

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

W przypadku nowej bazy danych nie ma danych do zmigrowania, a update-database polecenie jest znacznie bardziej prawdopodobne, aby ukończyć bez błędów. Aby uzyskać instrukcje dotyczące usuwania bazy danych, zobacz Jak usunąć bazę danych z programu Visual Studio 2012. Jeśli to podejście zostanie wykonane w celu kontynuowania pracy z samouczkiem, pomiń krok wdrażania na końcu tego samouczka lub wdróż w nowej lokacji i bazie danych. Jeśli wdrożysz aktualizację w tej samej lokacji, w której już wdrożono, program EF otrzyma ten sam błąd po automatycznym uruchomieniu migracji. Jeśli chcesz rozwiązać problem z błędem migracji, najlepszym zasobem jest jedno z forów programu Entity Framework lub StackOverflow.com.

Testowanie implementacji

Uruchom witrynę i wypróbuj różne strony. Wszystko działa tak samo jak wcześniej.

W Eksploratorze serwera rozwiń węzeł Połączenia danych\SchoolContext, a następnie tabele Student i Instruktor zobaczysz, że tabeleStudent i Instruktor zostały zastąpione przez tabelę Person. Rozwiń tabelę Person i zobaczysz, że zawiera ona wszystkie kolumny, które były używane w tabelach Student i Instruktor .

Kliknij prawym przyciskiem myszy tabelę Osoba, a następnie kliknij polecenie Pokaż dane tabeli , aby wyświetlić kolumnę dyskryminującą.

Na poniższym diagramie przedstawiono strukturę nowej bazy danych School:

School_database_diagram

Wdróż na platformie Azure

Ta sekcja wymaga ukończenia opcjonalnej sekcji Wdrażanie aplikacji na platformie Azure w części 3, sortowanie, filtrowanie i stronicowanie tej serii samouczków. Jeśli wystąpiły błędy migracji, które usunięto, usuwając bazę danych w projekcie lokalnym, pomiń ten krok; lub utwórz nową lokację i bazę danych oraz wdróż je w nowym środowisku.

  1. W programie Visual Studio kliknij prawym przyciskiem myszy projekt w Eksplorator rozwiązań i wybierz polecenie Publikuj z menu kontekstowego.

  2. Kliknij przycisk Opublikuj.

    Aplikacja internetowa zostanie otwarta w domyślnej przeglądarce.

  3. Przetestuj aplikację, aby sprawdzić, czy działa.

    Przy pierwszym uruchomieniu strony, która uzyskuje dostęp do bazy danych, platforma Entity Framework uruchamia wszystkie metody migracji Up wymagane do zapewnienia aktualności bazy danych z bieżącym modelem danych.

Uzyskiwanie kodu

Pobieranie ukończonego projektu

Dodatkowe zasoby

Linki do innych zasobów platformy Entity Framework można znaleźć w ASP.NET Dostęp do danych — zalecane zasoby.

Aby uzyskać więcej informacji na temat tej i innych struktur dziedziczenia, zobacz TPT Inheritance Pattern and TPH Inheritance Pattern on MSDN ( Wzorzec dziedziczenia TPT i wzorzec dziedziczenia TPH w witrynie MSDN). W następnym samouczku zobaczysz, jak obsługiwać różne stosunkowo zaawansowane scenariusze platformy Entity Framework.

Następne kroki

W tym samouczku zostały wykonane następujące czynności:

  • Naucz się mapować dziedziczenie do bazy danych
  • Utworzono klasę Person
  • Zaktualizowano instruktora i ucznia
  • Dodano osobę do modelu
  • Utworzone i zaktualizowane migracje
  • Przetestowano implementację
  • Wdrożone na platformie Azure

Przejdź do następnego artykułu, aby dowiedzieć się więcej o tematach, które są przydatne do zapoznania się z podstawami tworzenia ASP.NET aplikacji internetowych korzystających z programu Entity Framework Code First.