Udostępnij za pośrednictwem


Samouczek: tworzenie złożonego modelu danych — ASP.NET MVC za pomocą polecenia EF Core

W poprzednich samouczkach pracowaliśmy z prostym modelem danych, który składał się z trzech jednostek. W tym samouczku dodasz więcej jednostek i relacji, a następnie dostosujesz model danych, określając formatowanie, walidację i reguły mapowania bazy danych.

Po zakończeniu klasy jednostek składają się na ukończony model danych pokazany na poniższej ilustracji:

Entity diagram

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

  • Dostosowywanie modelu danych
  • Wprowadzanie zmian w jednostce Student
  • Tworzenie jednostki instruktora
  • Tworzenie jednostki OfficeAssignment
  • Modyfikowanie jednostki Course
  • Tworzenie jednostki Dział
  • Modyfikowanie jednostki Rejestracji
  • Aktualizowanie kontekstu bazy danych
  • Inicjuj bazę danych z danymi testowymi
  • Dodawanie migracji
  • Zmienianie parametry połączenia
  • Aktualizowanie bazy danych

Wymagania wstępne

Dostosowywanie modelu danych

W tej sekcji dowiesz się, jak dostosować model danych przy użyciu atrybutów określających reguły formatowania, walidacji i mapowania bazy danych. Następnie w kilku poniższych sekcjach utworzysz kompletny model danych Szkoły, dodając atrybuty do utworzonych już klas i tworząc nowe klasy dla pozostałych typów jednostek w modelu.

Atrybut DataType

W przypadku dat rejestracji uczniów wszystkie strony internetowe aktualnie wyświetlają godzinę wraz z datą, chociaż wszystko, o co chodzi w tym polu, to data. Za pomocą atrybutów adnotacji danych można wprowadzić jedną zmianę kodu, która naprawi format wyświetlania w każdym widoku, który pokazuje dane. Aby zobaczyć przykład tego, jak to zrobić, dodasz atrybut do EnrollmentDate właściwości w Student klasie.

W Models/Student.cspliku dodaj instrukcję using dla System.ComponentModel.DataAnnotations przestrzeni nazw i dodaj DataType atrybuty i DisplayFormat do EnrollmentDate właściwości, jak pokazano w poniższym przykładzie:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace ContosoUniversity.Models
{
    public class Student
    {
        public int ID { get; set; }
        public string LastName { get; set; }
        public string FirstMidName { get; set; }
        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        public DateTime EnrollmentDate { get; set; }

        public ICollection<Enrollment> Enrollments { get; set; }
    }
}

Atrybut DataType służy do określania typu danych, który jest bardziej szczegółowy niż typ wewnętrzny bazy danych. W tym przypadku chcemy śledzić tylko datę, a nie datę i godzinę. Wyliczenie DataType zawiera wiele typów danych, takich jak Data, Godzina, Telefon Number, Waluta, Adres e-mail i inne. Atrybut DataType może również umożliwić aplikacji automatyczne udostępnianie funkcji specyficznych dla typu. Na przykład mailto: można utworzyć link dla DataType.EmailAddresselementu , a selektor dat można udostępnić DataType.Date w przeglądarkach obsługujących kod HTML5. Atrybut DataType emituje atrybuty HTML 5 data- (wymawiane kreska danych), które przeglądarki HTML 5 mogą zrozumieć. Atrybuty DataType nie zapewniają żadnej walidacji.

DataType.Date nie określa formatu wyświetlanej daty. Domyślnie pole danych jest wyświetlane zgodnie z domyślnymi formatami na podstawie informacji o kulturze serwera.

Atrybut DisplayFormat jest używany do jawnego określenia formatu daty:

[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]

Ustawienie ApplyFormatInEditMode określa, że formatowanie powinno być również stosowane, gdy wartość jest wyświetlana w polu tekstowym do edycji. (Możesz nie chcieć tego dla niektórych pól — na przykład w przypadku wartości walutowych symbol waluty w polu tekstowym do edycji).

Można użyć atrybutu DisplayFormat samodzielnie, ale zazwyczaj dobrym pomysłem jest również użycie atrybutu DataType . Atrybut DataType przekazuje semantyka danych w przeciwieństwie do sposobu renderowania ich na ekranie i zapewnia następujące korzyści, których nie otrzymujesz za pomocą DisplayFormatpolecenia :

  • Przeglądarka może włączyć funkcje HTML5 (na przykład w celu wyświetlenia kontrolki kalendarza, symbolu waluty odpowiedniego dla ustawień regionalnych, linków poczty e-mail, weryfikacji danych wejściowych po stronie klienta itp.).

  • Domyślnie przeglądarka będzie renderować dane przy użyciu poprawnego formatu na podstawie ustawień regionalnych.

Aby uzyskać więcej informacji, zobacz dokumentację pomocnika tagów <wejściowych>.

Uruchom aplikację, przejdź do strony Indeks uczniów i zwróć uwagę, że czasy nie są już wyświetlane dla dat rejestracji. To samo będzie dotyczyć każdego widoku korzystającego z modelu Student.

Students index page showing dates without times

Atrybut StringLength

Można również określić reguły walidacji danych i komunikaty o błędach walidacji przy użyciu atrybutów. Atrybut StringLength ustawia maksymalną długość bazy danych i zapewnia weryfikację po stronie klienta i po stronie serwera dla ASP.NET Core MVC. Można również określić minimalną długość ciągu w tym atrybucie, ale minimalna wartość nie ma wpływu na schemat bazy danych.

Załóżmy, że chcesz mieć pewność, że użytkownicy nie wprowadzają więcej niż 50 znaków dla nazwy. Aby dodać to ograniczenie, dodaj StringLength atrybuty do LastName właściwości i FirstMidName , jak pokazano w poniższym przykładzie:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace ContosoUniversity.Models
{
    public class Student
    {
        public int ID { get; set; }
        [StringLength(50)]
        public string LastName { get; set; }
        [StringLength(50)]
        public string FirstMidName { get; set; }
        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        public DateTime EnrollmentDate { get; set; }

        public ICollection<Enrollment> Enrollments { get; set; }
    }
}

Atrybut StringLength nie uniemożliwi użytkownikowi wprowadzania białych znaków dla nazwy. Możesz użyć atrybutu RegularExpression , aby zastosować ograniczenia do danych wejściowych. Na przykład poniższy kod wymaga, aby pierwszy znak był wielkimi literami, a pozostałe znaki mają postać alfabetyczną:

[RegularExpression(@"^[A-Z]+[a-zA-Z]*$")]

Atrybut MaxLength zapewnia funkcjonalność podobną do atrybutu StringLength , ale nie zapewnia weryfikacji po stronie klienta.

Model bazy danych zmienił się teraz w sposób, który wymaga zmiany schematu bazy danych. Użyjesz migracji, aby zaktualizować schemat bez utraty danych, które mogły zostać dodane do bazy danych przy użyciu interfejsu użytkownika aplikacji.

Zapisz zmiany i skompiluj projekt. Następnie otwórz okno polecenia w folderze projektu i wprowadź następujące polecenia:

dotnet ef migrations add MaxLengthOnNames
dotnet ef database update

Polecenie migrations add ostrzega, że może wystąpić utrata danych, ponieważ zmiana sprawia, że maksymalna długość jest krótsza dla dwóch kolumn. Migracje tworzą plik o nazwie <timeStamp>_MaxLengthOnNames.cs. Ten plik zawiera kod w metodzie Up , która zaktualizuje bazę danych tak, aby odpowiadała bieżącemu modelowi danych. Polecenie database update uruchomiło ten kod.

Sygnatura czasowa poprzedzona nazwą pliku migracji jest używana przez program Entity Framework do zamawiania migracji. Można utworzyć wiele migracji przed uruchomieniem polecenia update-database, a następnie wszystkie migracje są stosowane w kolejności, w której zostały utworzone.

Uruchom aplikację, wybierz kartę Uczniowie , kliknij pozycję Utwórz nowy i spróbuj wprowadzić nazwę dłuższą niż 50 znaków. Aplikacja powinna uniemożliwić wykonanie tej czynności.

Atrybut Kolumna

Możesz również użyć atrybutów, aby kontrolować sposób mapowania klas i właściwości na bazę danych. Załóżmy, że użyto nazwy FirstMidName pola imię, ponieważ pole może również zawierać nazwę środkową. Jednak chcesz, aby kolumna bazy danych miała nazwę FirstName, ponieważ użytkownicy, którzy będą pisać zapytania ad hoc względem bazy danych, są przyzwyczajeni do tej nazwy. Aby ustawić to mapowanie, możesz użyć atrybutu Column .

Atrybut Column określa, że po utworzeniu bazy danych kolumna Student tabeli mapowania na FirstMidName właściwość będzie mieć nazwę FirstName. Innymi słowy, gdy kod odwołuje się do Student.FirstMidName, dane pochodzą z tabeli lub zostaną zaktualizowane w FirstName kolumnie Student tabeli. Jeśli nie określisz nazw kolumn, mają one taką samą nazwę jak nazwa właściwości.

Student.cs W pliku dodaj instrukcję using i System.ComponentModel.DataAnnotations.Schema dodaj atrybut nazwy kolumny do FirstMidName właściwości, jak pokazano w poniższym wyróżnionym kodzie:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace ContosoUniversity.Models
{
    public class Student
    {
        public int ID { get; set; }
        [StringLength(50)]
        public string LastName { get; set; }
        [StringLength(50)]
        [Column("FirstName")]
        public string FirstMidName { get; set; }
        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        public DateTime EnrollmentDate { get; set; }

        public ICollection<Enrollment> Enrollments { get; set; }
    }
}

Dodanie atrybutu Column zmienia model kopii zapasowej SchoolContextelementu , aby nie był zgodny z bazą danych.

Zapisz zmiany i skompiluj projekt. Następnie otwórz okno polecenia w folderze projektu i wprowadź następujące polecenia, aby utworzyć inną migrację:

dotnet ef migrations add ColumnFirstName
dotnet ef database update

W programie SQL Server Eksplorator obiektów otwórz projektanta tabeli Student, klikając dwukrotnie tabelę Student.

Students table in SSOX after migrations

Przed zastosowaniem dwóch pierwszych migracji kolumny nazw były typu nvarchar(MAX). Są teraz nvarchar(50), a nazwa kolumny zmieniła się z FirstMidName na FirstName.

Uwaga

Jeśli spróbujesz skompilować przed zakończeniem tworzenia wszystkich klas jednostek w poniższych sekcjach, mogą wystąpić błędy kompilatora.

Zmiany w jednostce Student

Student entity

W Models/Student.cspliku zastąp kod dodany wcześniej następującym kodem. Zmiany są wyróżnione.

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace ContosoUniversity.Models
{
    public class Student
    {
        public int ID { get; set; }
        [Required]
        [StringLength(50)]
        [Display(Name = "Last Name")]
        public string LastName { get; set; }
        [Required]
        [StringLength(50)]
        [Column("FirstName")]
        [Display(Name = "First Name")]
        public string FirstMidName { get; set; }
        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        [Display(Name = "Enrollment Date")]
        public DateTime EnrollmentDate { get; set; }
        [Display(Name = "Full Name")]
        public string FullName
        {
            get
            {
                return LastName + ", " + FirstMidName;
            }
        }

        public ICollection<Enrollment> Enrollments { get; set; }
    }
}

Wymagany atrybut

Atrybut Required sprawia, że właściwości nazwy są wymagane pola. Atrybut Required nie jest wymagany w przypadku typów bez wartości null, takich jak typy wartości (DateTime, int, double, float itp.). Typy, które nie mogą mieć wartości null, są automatycznie traktowane jako wymagane pola.

Atrybut Required musi być używany z elementem MinimumLength MinimumLength , aby można było wymusić.

[Display(Name = "Last Name")]
[Required]
[StringLength(50, MinimumLength=2)]
public string LastName { get; set; }

Atrybut Wyświetlania

Atrybut Display określa, że podpis pól tekstowych powinny być "Imię", "Nazwisko", "Imię", "Imię", "Pełna nazwa" i "Data rejestracji" zamiast nazwy właściwości w każdym wystąpieniu (które nie ma spacji dzielącej wyrazy).

Właściwość obliczeniowa FullName

FullName jest właściwością obliczeniową, która zwraca wartość utworzoną przez łączenie dwóch innych właściwości. W związku z tym ma ona tylko metodę pobierania, a w bazie danych nie FullName zostanie wygenerowana żadna kolumna.

Tworzenie jednostki instruktora

Instructor entity

Utwórz Models/Instructor.csplik , zastępując kod szablonu następującym kodem:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace ContosoUniversity.Models
{
    public class Instructor
    {
        public int ID { get; set; }

        [Required]
        [Display(Name = "Last Name")]
        [StringLength(50)]
        public string LastName { get; set; }

        [Required]
        [Column("FirstName")]
        [Display(Name = "First Name")]
        [StringLength(50)]
        public string FirstMidName { get; set; }

        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        [Display(Name = "Hire Date")]
        public DateTime HireDate { get; set; }

        [Display(Name = "Full Name")]
        public string FullName
        {
            get { return LastName + ", " + FirstMidName; }
        }

        public ICollection<CourseAssignment> CourseAssignments { get; set; }
        public OfficeAssignment OfficeAssignment { get; set; }
    }
}

Zwróć uwagę, że kilka właściwości jest takich samych w jednostkach Student i Instruktor. W samouczku Implementowanie dziedziczenia w dalszej części tej serii refaktoryzujesz ten kod, aby wyeliminować nadmiarowość.

Można umieścić wiele atrybutów w jednym wierszu, aby można było również napisać HireDate atrybuty w następujący sposób:

[DataType(DataType.Date),Display(Name = "Hire Date"),DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]

Właściwości nawigacji CourseAssignments i OfficeAssignment

Właściwości CourseAssignments i OfficeAssignment to właściwości nawigacji.

Instruktor może uczyć dowolną liczbę kursów, dlatego CourseAssignments jest definiowany jako kolekcja.

public ICollection<CourseAssignment> CourseAssignments { get; set; }

Jeśli właściwość nawigacji może zawierać wiele jednostek, jej typ musi być listą, w której można dodawać, usuwać i aktualizować wpisy. Można określić ICollection<T> lub typ, taki jak List<T> lub HashSet<T>. Jeśli określisz ICollection<T>wartość , program EF domyślnie tworzy HashSet<T> kolekcję.

Powód, dla którego te jednostki zostały CourseAssignment wyjaśnione poniżej w sekcji dotyczącej relacji wiele-do-wielu.

Reguły biznesowe firmy Contoso University stwierdzają, że instruktor może mieć tylko jedno biuro, więc OfficeAssignment właściwość posiada pojedynczą jednostkę OfficeAssignment (która może mieć wartość null, jeśli nie przypisano urzędu).

public OfficeAssignment OfficeAssignment { get; set; }

Tworzenie jednostki OfficeAssignment

OfficeAssignment entity

Utwórz Models/OfficeAssignment.cs za pomocą następującego kodu:

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace ContosoUniversity.Models
{
    public class OfficeAssignment
    {
        [Key]
        public int InstructorID { get; set; }
        [StringLength(50)]
        [Display(Name = "Office Location")]
        public string Location { get; set; }

        public Instructor Instructor { get; set; }
    }
}

Atrybut Klucz

Istnieje relacja jeden do zera lub jednego między jednostkami Instructor i OfficeAssignment . Przypisanie biura istnieje tylko w odniesieniu do instruktora, do której jest przypisany, a zatem jego klucz podstawowy jest również kluczem obcym Instructor jednostki. Jednak program Entity Framework nie może automatycznie rozpoznać InstructorID jako klucza podstawowego tej jednostki, ponieważ jego nazwa nie jest zgodna z konwencją ID nazewnictwa ani classnameID . W związku z tym Key atrybut jest używany do identyfikowania go jako klucza:

[Key]
public int InstructorID { get; set; }

Możesz również użyć atrybutu Key , jeśli jednostka ma własny klucz podstawowy, ale chcesz nazwać właściwość inną niż classnameID lub ID.

Domyślnie program EF traktuje klucz jako niegenerowany przez bazę danych, ponieważ kolumna służy do identyfikowania relacji.

Właściwość nawigacji instruktora

Jednostka Instruktor ma właściwość nawigacji dopuszczającej OfficeAssignment wartość null (ponieważ instruktor może nie mieć przypisania pakietu Office), a jednostka OfficeAssignment ma właściwość nawigacji bez wartości null Instructor (ponieważ przypisanie pakietu Office nie może istnieć bez instruktora — InstructorID jest niepuste). Gdy jednostka Instruktor ma powiązaną jednostkę OfficeAssignment, każda jednostka będzie mieć odwołanie do drugiej w jej właściwości nawigacji.

Możesz umieścić [Required] atrybut we właściwości nawigacji instruktora, aby określić, że musi istnieć powiązany instruktor, ale nie musisz tego robić, ponieważ InstructorID klucz obcy (który jest również kluczem do tej tabeli) nie może mieć wartości null.

Modyfikowanie jednostki Course

Course entity

W Models/Course.cspliku zastąp kod dodany wcześniej następującym kodem. Zmiany są wyróżnione.

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace ContosoUniversity.Models
{
    public class Course
    {
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        [Display(Name = "Number")]
        public int CourseID { get; set; }

        [StringLength(50, MinimumLength = 3)]
        public string Title { get; set; }

        [Range(0, 5)]
        public int Credits { get; set; }

        public int DepartmentID { get; set; }

        public Department Department { get; set; }
        public ICollection<Enrollment> Enrollments { get; set; }
        public ICollection<CourseAssignment> CourseAssignments { get; set; }
    }
}

Jednostka kursu ma właściwość DepartmentID klucza obcego, która wskazuje powiązaną Department jednostkę Department i ma właściwość nawigacji.

Program Entity Framework nie wymaga dodania właściwości klucza obcego do modelu danych, jeśli masz właściwość nawigacji dla powiązanej jednostki. Program EF automatycznie tworzy klucze obce w bazie danych niezależnie od potrzeb i tworzy dla nich właściwości w tle. Jednak posiadanie klucza obcego w modelu danych może sprawić, że aktualizacje będą prostsze i bardziej wydajne. Na przykład podczas pobierania Course jednostki do edycji jednostka ma wartość null, Department jeśli nie zostanie załadowana, więc podczas aktualizowania Course jednostki trzeba będzie najpierw pobrać Department jednostkę. Jeśli właściwość DepartmentID klucza obcego jest uwzględniona w modelu danych, nie musisz pobierać Department jednostki przed aktualizacją.

Atrybut DatabaseGenerated

Atrybut DatabaseGenerated z parametrem None właściwości CourseID określa, że wartości klucza podstawowego są dostarczane przez użytkownika, a nie generowane przez bazę danych.

[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Display(Name = "Number")]
public int CourseID { get; set; }

Domyślnie program Entity Framework zakłada, że wartości klucza podstawowego są generowane przez bazę danych. To jest to, czego potrzebujesz w większości scenariuszy. Jednak w przypadku Course jednostek użyjesz określonego przez użytkownika numeru kursu, takiego jak seria 1000 dla jednego działu, serii 2000 dla innego działu itd.

Atrybut DatabaseGenerated może również służyć do generowania wartości domyślnych, jak w przypadku kolumn bazy danych używanych do rejestrowania daty utworzenia lub zaktualizowania wiersza. Aby uzyskać więcej informacji, zobacz Wygenerowane właściwości.

Właściwości klucza obcego i nawigacji

Właściwości klucza obcego i właściwości nawigacji w jednostce Course odzwierciedlają następujące relacje:

Kurs jest przypisywany do jednego działu, więc istnieje DepartmentID klucz obcy i Department właściwość nawigacji z powodów wymienionych powyżej.

public int DepartmentID { get; set; }
public Department Department { get; set; }

Kurs może mieć dowolną liczbę zarejestrowanych w nim uczniów, więc Enrollments właściwość nawigacji jest kolekcją:

public ICollection<Enrollment> Enrollments { get; set; }

Kurs może być nauczany przez wielu instruktorów, więc CourseAssignments właściwość nawigacji jest kolekcją (typ CourseAssignment jest wyjaśniony później):

public ICollection<CourseAssignment> CourseAssignments { get; set; }

Tworzenie jednostki Dział

Department entity

Utwórz Models/Department.cs za pomocą następującego kodu:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace ContosoUniversity.Models
{
    public class Department
    {
        public int DepartmentID { get; set; }

        [StringLength(50, MinimumLength = 3)]
        public string Name { get; set; }

        [DataType(DataType.Currency)]
        [Column(TypeName = "money")]
        public decimal Budget { get; set; }

        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        [Display(Name = "Start Date")]
        public DateTime StartDate { get; set; }

        public int? InstructorID { get; set; }

        public Instructor Administrator { get; set; }
        public ICollection<Course> Courses { get; set; }
    }
}

Atrybut Kolumna

Wcześniej użyto atrybutu Column do zmiany mapowania nazw kolumn. W kodzie Department jednostki atrybut jest używany do zmiany mapowania typu danych SQL, Column aby kolumna została zdefiniowana przy użyciu typu programu SQL Server money w bazie danych:

[Column(TypeName="money")]
public decimal Budget { get; set; }

Mapowanie kolumn nie jest zwykle wymagane, ponieważ program Entity Framework wybiera odpowiedni typ danych programu SQL Server na podstawie typu CLR zdefiniowanego dla właściwości. Typ CLR decimal jest mapowy na typ programu SQL Server decimal . Ale w tym przypadku wiesz, że kolumna będzie przechowywać kwoty waluty, a typ danych pieniężnych jest bardziej odpowiedni dla tego.

Właściwości klucza obcego i nawigacji

Właściwości klucza obcego i nawigacji odzwierciedlają następujące relacje:

Dział może lub nie ma administratora, a administrator jest zawsze instruktorem. InstructorID W związku z tym właściwość jest dołączana jako klucz obcy do jednostki Instruktor, a znak zapytania jest dodawany po int oznaczeniu typu, aby oznaczyć właściwość jako dopuszczaną wartość null. Właściwość nawigacji ma nazwę Administrator , ale zawiera jednostkę Instruktor:

public int? InstructorID { get; set; }
public Instructor Administrator { get; set; }

Dział może mieć wiele kursów, więc istnieje właściwość nawigacji Kursy:

public ICollection<Course> Courses { get; set; }

Uwaga

Zgodnie z konwencją platforma Entity Framework umożliwia kaskadowe usuwanie kluczy obcych bez wartości null i relacje wiele-do-wielu. Może to spowodować cykliczne reguły usuwania kaskadowego, co spowoduje wyjątek podczas próby dodania migracji. Jeśli na przykład właściwość nie zostanie zdefiniowana Department.InstructorID jako dopuszczana do wartości null, program EF skonfiguruje regułę usuwania kaskadowego, aby usunąć dział po usunięciu instruktora, co nie jest tym, co ma się zdarzyć. Jeśli reguły biznesowe wymagały InstructorID , aby właściwość nie mogła mieć wartości null, należy użyć następującej instrukcji płynnego interfejsu API, aby wyłączyć usuwanie kaskadowe w relacji:

modelBuilder.Entity<Department>()
   .HasOne(d => d.Administrator)
   .WithMany()
   .OnDelete(DeleteBehavior.Restrict)

Modyfikowanie jednostki Rejestracji

Enrollment entity

W Models/Enrollment.cspliku zastąp kod dodany wcześniej następującym kodem:

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace ContosoUniversity.Models
{
    public enum Grade
    {
        A, B, C, D, F
    }

    public class Enrollment
    {
        public int EnrollmentID { get; set; }
        public int CourseID { get; set; }
        public int StudentID { get; set; }
        [DisplayFormat(NullDisplayText = "No grade")]
        public Grade? Grade { get; set; }

        public Course Course { get; set; }
        public Student Student { get; set; }
    }
}

Właściwości klucza obcego i nawigacji

Właściwości klucza obcego i właściwości nawigacji odzwierciedlają następujące relacje:

Rekord rejestracji dotyczy jednego kursu, więc istnieje właściwość klucza obcego CourseID Course i właściwość nawigacji:

public int CourseID { get; set; }
public Course Course { get; set; }

Rekord rejestracji dotyczy jednego ucznia, więc istnieje właściwość klucza obcego StudentID Student i właściwość nawigacji:

public int StudentID { get; set; }
public Student Student { get; set; }

Relacje wiele-do-wielu

Istnieje relacja wiele do wielu między jednostkami Student i Course , a Enrollment jednostka działa jako tabela sprzężenia wiele-do-wielu z ładunkiem w bazie danych. "Z ładunkiem" oznacza, że Enrollment tabela zawiera dodatkowe dane oprócz kluczy obcych dla tabel sprzężonych (w tym przypadku klucz podstawowy i Grade właściwość).

Poniższa ilustracja przedstawia wygląd tych relacji na diagramie jednostki. (Ten diagram został wygenerowany przy użyciu narzędzi Entity Framework Power Tools for EF 6.x; tworzenie diagramu nie jest częścią tego samouczka. Jest on po prostu używany jako ilustracja).

Student-Course many to many relationship

Każda linia relacji ma wartość 1 na jednym końcu i gwiazdkę (*) z drugiej strony wskazującą relację jeden do wielu.

Enrollment Jeśli tabela nie zawierała informacji o klasie, musi zawierać tylko dwa klucze CourseID obce i StudentID. W takim przypadku byłaby to tabela sprzężenia wiele-do-wielu bez ładunku (lub czystej tabeli sprzężeń) w bazie danych. Jednostki Instructor i Course mają taką relację wiele do wielu, a następnym krokiem jest utworzenie klasy jednostki, która będzie działać jako tabela sprzężenia bez ładunku.

EF Core obsługuje niejawne tabele sprzężenia dla relacji wiele-do-wielu, ale ten korepetytor nie został zaktualizowany do używania niejawnej tabeli sprzężenia. Zobacz Relacje wiele-do-wielu— wersja strony tego samouczka, Razor która została zaktualizowana.

Jednostka CourseAssignment

CourseAssignment entity

Utwórz Models/CourseAssignment.cs za pomocą następującego kodu:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace ContosoUniversity.Models
{
    public class CourseAssignment
    {
        public int InstructorID { get; set; }
        public int CourseID { get; set; }
        public Instructor Instructor { get; set; }
        public Course Course { get; set; }
    }
}

Nazwy jednostek sprzężenia

Tabela sprzężenia jest wymagana w bazie danych dla relacji Instruktor-to-Courses wiele-do-wielu i musi być reprezentowana przez zestaw jednostek. Często nazywa się jednostkę EntityName1EntityName2sprzężenia , która w tym przypadku będzie miała wartość CourseInstructor. Zalecamy jednak wybranie nazwy, która opisuje relację. Modele danych zaczynają się proste i rosną, a sprzężenia bez ładunku często są ładowane później. Jeśli zaczniesz od nazwy jednostki opisowej, nie musisz później zmieniać nazwy. Najlepiej, aby jednostka sprzężenia miała własną naturalną (prawdopodobnie pojedynczą nazwę) w domenie biznesowej. Na przykład książki i klienci mogą być połączone za pośrednictwem klasyfikacji. W przypadku tej relacji CourseAssignment jest lepszym wyborem niż CourseInstructor.

Klucz złożony

Ponieważ klucze obce nie są dopuszczane do wartości null i razem jednoznacznie identyfikują każdy wiersz tabeli, nie ma potrzeby oddzielnego klucza podstawowego. Właściwości InstructorID i CourseID powinny działać jako złożony klucz podstawowy. Jedynym sposobem identyfikowania złożonych kluczy podstawowych do platformy EF jest użycie płynnego interfejsu API (nie można go wykonać przy użyciu atrybutów). W następnej sekcji zobaczysz, jak skonfigurować złożony klucz podstawowy.

Klucz złożony gwarantuje, że chociaż można mieć wiele wierszy dla jednego kursu i wiele wierszy dla jednego instruktora, nie można mieć wielu wierszy dla tego samego instruktora i kursu. Jednostka Enrollment sprzężenia definiuje własny klucz podstawowy, więc możliwe są duplikaty tego rodzaju. Aby zapobiec takim duplikatom, można dodać unikatowy indeks w polach klucza obcego lub skonfigurować przy użyciu Enrollment podstawowego klucza złożonego podobnego do CourseAssignment. Aby uzyskać więcej informacji, zobacz Indeksy.

Aktualizowanie kontekstu bazy danych

Dodaj następujący wyróżniony kod do Data/SchoolContext.cs pliku:

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

        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<CourseAssignment>()
                .HasKey(c => new { c.CourseID, c.InstructorID });
        }
    }
}

Ten kod dodaje nowe jednostki i konfiguruje złożony klucz podstawowy jednostki CourseAssignment.

Informacje o alternatywnej wersji interfejsu API

Kod w OnModelCreating metodzie DbContext klasy używa płynnego interfejsu API do konfigurowania zachowania platformy EF. Interfejs API jest nazywany "fluent", ponieważ jest często używany przez ciągowanie serii wywołań metod w jedną instrukcję, jak w tym przykładzie EF Core z dokumentacji:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Property(b => b.Url)
        .IsRequired();
}

W tym samouczku używasz płynnego interfejsu API tylko do mapowania bazy danych, których nie można wykonać z atrybutami. Można jednak również użyć płynnego interfejsu API, aby określić większość reguł formatowania, walidacji i mapowania, które można wykonać przy użyciu atrybutów. Niektóre atrybuty, takie jak MinimumLength nie można zastosować za pomocą płynnego interfejsu API. Jak wspomniano wcześniej, MinimumLength nie zmienia schematu, stosuje tylko regułę weryfikacji po stronie klienta i serwera.

Niektórzy deweloperzy wolą używać płynnego interfejsu API wyłącznie tak, aby mogli zachować klasy jednostek "czyste". Jeśli chcesz, możesz mieszać atrybuty i płynny interfejs API. Istnieje kilka dostosowań, które można wykonać tylko przy użyciu płynnego interfejsu API, ale ogólnie zalecaną praktyką jest wybranie jednego z tych dwóch podejść i użycie tej spójnej ilości, jak to możliwe. Jeśli używasz obu tych elementów, należy pamiętać, że wszędzie tam, gdzie występuje konflikt, interfejs API Fluent zastępuje atrybuty.

Aby uzyskać więcej informacji na temat atrybutów i płynnego interfejsu API, zobacz Metody konfiguracji.

Diagram jednostki przedstawiający relacje

Na poniższej ilustracji przedstawiono diagram utworzony przez narzędzia Entity Framework Power Tools dla ukończonego modelu School.

Entity diagram

Oprócz linii relacji jeden do wielu (od 1 do *) można zobaczyć tutaj wiersz relacji jeden do zera lub jednego (od 1 do 0,1) między jednostkami i OfficeAssignment a wierszem relacji zero-lub jeden do wielu (od 0,1 do *) między Instructor jednostkami Instruktor i Dział.

Inicjuj bazę danych z danymi testowymi

Zastąp kod w Data/DbInitializer.cs pliku następującym kodem, aby podać dane inicjuj dla nowo utworzonych jednostek.

using System;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using ContosoUniversity.Models;

namespace ContosoUniversity.Data
{
    public static class DbInitializer
    {
        public static void Initialize(SchoolContext context)
        {
            //context.Database.EnsureCreated();

            // Look for any students.
            if (context.Students.Any())
            {
                return;   // DB has been seeded
            }

            var students = new Student[]
            {
                new Student { FirstMidName = "Carson",   LastName = "Alexander",
                    EnrollmentDate = DateTime.Parse("2010-09-01") },
                new Student { FirstMidName = "Meredith", LastName = "Alonso",
                    EnrollmentDate = DateTime.Parse("2012-09-01") },
                new Student { FirstMidName = "Arturo",   LastName = "Anand",
                    EnrollmentDate = DateTime.Parse("2013-09-01") },
                new Student { FirstMidName = "Gytis",    LastName = "Barzdukas",
                    EnrollmentDate = DateTime.Parse("2012-09-01") },
                new Student { FirstMidName = "Yan",      LastName = "Li",
                    EnrollmentDate = DateTime.Parse("2012-09-01") },
                new Student { FirstMidName = "Peggy",    LastName = "Justice",
                    EnrollmentDate = DateTime.Parse("2011-09-01") },
                new Student { FirstMidName = "Laura",    LastName = "Norman",
                    EnrollmentDate = DateTime.Parse("2013-09-01") },
                new Student { FirstMidName = "Nino",     LastName = "Olivetto",
                    EnrollmentDate = DateTime.Parse("2005-09-01") }
            };

            foreach (Student s in students)
            {
                context.Students.Add(s);
            }
            context.SaveChanges();

            var instructors = new Instructor[]
            {
                new Instructor { FirstMidName = "Kim",     LastName = "Abercrombie",
                    HireDate = DateTime.Parse("1995-03-11") },
                new Instructor { FirstMidName = "Fadi",    LastName = "Fakhouri",
                    HireDate = DateTime.Parse("2002-07-06") },
                new Instructor { FirstMidName = "Roger",   LastName = "Harui",
                    HireDate = DateTime.Parse("1998-07-01") },
                new Instructor { FirstMidName = "Candace", LastName = "Kapoor",
                    HireDate = DateTime.Parse("2001-01-15") },
                new Instructor { FirstMidName = "Roger",   LastName = "Zheng",
                    HireDate = DateTime.Parse("2004-02-12") }
            };

            foreach (Instructor i in instructors)
            {
                context.Instructors.Add(i);
            }
            context.SaveChanges();

            var departments = new Department[]
            {
                new Department { Name = "English",     Budget = 350000,
                    StartDate = DateTime.Parse("2007-09-01"),
                    InstructorID  = instructors.Single( i => i.LastName == "Abercrombie").ID },
                new Department { Name = "Mathematics", Budget = 100000,
                    StartDate = DateTime.Parse("2007-09-01"),
                    InstructorID  = instructors.Single( i => i.LastName == "Fakhouri").ID },
                new Department { Name = "Engineering", Budget = 350000,
                    StartDate = DateTime.Parse("2007-09-01"),
                    InstructorID  = instructors.Single( i => i.LastName == "Harui").ID },
                new Department { Name = "Economics",   Budget = 100000,
                    StartDate = DateTime.Parse("2007-09-01"),
                    InstructorID  = instructors.Single( i => i.LastName == "Kapoor").ID }
            };

            foreach (Department d in departments)
            {
                context.Departments.Add(d);
            }
            context.SaveChanges();

            var courses = new Course[]
            {
                new Course {CourseID = 1050, Title = "Chemistry",      Credits = 3,
                    DepartmentID = departments.Single( s => s.Name == "Engineering").DepartmentID
                },
                new Course {CourseID = 4022, Title = "Microeconomics", Credits = 3,
                    DepartmentID = departments.Single( s => s.Name == "Economics").DepartmentID
                },
                new Course {CourseID = 4041, Title = "Macroeconomics", Credits = 3,
                    DepartmentID = departments.Single( s => s.Name == "Economics").DepartmentID
                },
                new Course {CourseID = 1045, Title = "Calculus",       Credits = 4,
                    DepartmentID = departments.Single( s => s.Name == "Mathematics").DepartmentID
                },
                new Course {CourseID = 3141, Title = "Trigonometry",   Credits = 4,
                    DepartmentID = departments.Single( s => s.Name == "Mathematics").DepartmentID
                },
                new Course {CourseID = 2021, Title = "Composition",    Credits = 3,
                    DepartmentID = departments.Single( s => s.Name == "English").DepartmentID
                },
                new Course {CourseID = 2042, Title = "Literature",     Credits = 4,
                    DepartmentID = departments.Single( s => s.Name == "English").DepartmentID
                },
            };

            foreach (Course c in courses)
            {
                context.Courses.Add(c);
            }
            context.SaveChanges();

            var officeAssignments = new OfficeAssignment[]
            {
                new OfficeAssignment {
                    InstructorID = instructors.Single( i => i.LastName == "Fakhouri").ID,
                    Location = "Smith 17" },
                new OfficeAssignment {
                    InstructorID = instructors.Single( i => i.LastName == "Harui").ID,
                    Location = "Gowan 27" },
                new OfficeAssignment {
                    InstructorID = instructors.Single( i => i.LastName == "Kapoor").ID,
                    Location = "Thompson 304" },
            };

            foreach (OfficeAssignment o in officeAssignments)
            {
                context.OfficeAssignments.Add(o);
            }
            context.SaveChanges();

            var courseInstructors = new CourseAssignment[]
            {
                new CourseAssignment {
                    CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID,
                    InstructorID = instructors.Single(i => i.LastName == "Kapoor").ID
                    },
                new CourseAssignment {
                    CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID,
                    InstructorID = instructors.Single(i => i.LastName == "Harui").ID
                    },
                new CourseAssignment {
                    CourseID = courses.Single(c => c.Title == "Microeconomics" ).CourseID,
                    InstructorID = instructors.Single(i => i.LastName == "Zheng").ID
                    },
                new CourseAssignment {
                    CourseID = courses.Single(c => c.Title == "Macroeconomics" ).CourseID,
                    InstructorID = instructors.Single(i => i.LastName == "Zheng").ID
                    },
                new CourseAssignment {
                    CourseID = courses.Single(c => c.Title == "Calculus" ).CourseID,
                    InstructorID = instructors.Single(i => i.LastName == "Fakhouri").ID
                    },
                new CourseAssignment {
                    CourseID = courses.Single(c => c.Title == "Trigonometry" ).CourseID,
                    InstructorID = instructors.Single(i => i.LastName == "Harui").ID
                    },
                new CourseAssignment {
                    CourseID = courses.Single(c => c.Title == "Composition" ).CourseID,
                    InstructorID = instructors.Single(i => i.LastName == "Abercrombie").ID
                    },
                new CourseAssignment {
                    CourseID = courses.Single(c => c.Title == "Literature" ).CourseID,
                    InstructorID = instructors.Single(i => i.LastName == "Abercrombie").ID
                    },
            };

            foreach (CourseAssignment ci in courseInstructors)
            {
                context.CourseAssignments.Add(ci);
            }
            context.SaveChanges();

            var enrollments = new Enrollment[]
            {
                new Enrollment {
                    StudentID = students.Single(s => s.LastName == "Alexander").ID,
                    CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID,
                    Grade = Grade.A
                },
                    new Enrollment {
                    StudentID = students.Single(s => s.LastName == "Alexander").ID,
                    CourseID = courses.Single(c => c.Title == "Microeconomics" ).CourseID,
                    Grade = Grade.C
                    },
                    new Enrollment {
                    StudentID = students.Single(s => s.LastName == "Alexander").ID,
                    CourseID = courses.Single(c => c.Title == "Macroeconomics" ).CourseID,
                    Grade = Grade.B
                    },
                    new Enrollment {
                        StudentID = students.Single(s => s.LastName == "Alonso").ID,
                    CourseID = courses.Single(c => c.Title == "Calculus" ).CourseID,
                    Grade = Grade.B
                    },
                    new Enrollment {
                        StudentID = students.Single(s => s.LastName == "Alonso").ID,
                    CourseID = courses.Single(c => c.Title == "Trigonometry" ).CourseID,
                    Grade = Grade.B
                    },
                    new Enrollment {
                    StudentID = students.Single(s => s.LastName == "Alonso").ID,
                    CourseID = courses.Single(c => c.Title == "Composition" ).CourseID,
                    Grade = Grade.B
                    },
                    new Enrollment {
                    StudentID = students.Single(s => s.LastName == "Anand").ID,
                    CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID
                    },
                    new Enrollment {
                    StudentID = students.Single(s => s.LastName == "Anand").ID,
                    CourseID = courses.Single(c => c.Title == "Microeconomics").CourseID,
                    Grade = Grade.B
                    },
                new Enrollment {
                    StudentID = students.Single(s => s.LastName == "Barzdukas").ID,
                    CourseID = courses.Single(c => c.Title == "Chemistry").CourseID,
                    Grade = Grade.B
                    },
                    new Enrollment {
                    StudentID = students.Single(s => s.LastName == "Li").ID,
                    CourseID = courses.Single(c => c.Title == "Composition").CourseID,
                    Grade = Grade.B
                    },
                    new Enrollment {
                    StudentID = students.Single(s => s.LastName == "Justice").ID,
                    CourseID = courses.Single(c => c.Title == "Literature").CourseID,
                    Grade = Grade.B
                    }
            };

            foreach (Enrollment e in enrollments)
            {
                var enrollmentInDataBase = context.Enrollments.Where(
                    s =>
                            s.Student.ID == e.StudentID &&
                            s.Course.CourseID == e.CourseID).SingleOrDefault();
                if (enrollmentInDataBase == null)
                {
                    context.Enrollments.Add(e);
                }
            }
            context.SaveChanges();
        }
    }
}

Jak pokazano w pierwszym samouczku, większość tego kodu po prostu tworzy nowe obiekty jednostki i ładuje przykładowe dane do właściwości zgodnie z wymaganiami dotyczącymi testowania. Zwróć uwagę, jak są obsługiwane relacje wiele-do-wielu: kod tworzy relacje, tworząc jednostki w Enrollments zestawach jednostek i CourseAssignment sprzężenia.

Dodawanie migracji

Zapisz zmiany i skompiluj projekt. Następnie otwórz okno polecenia w folderze projektu i wprowadź migrations add polecenie (nie wykonaj jeszcze polecenia update-database):

dotnet ef migrations add ComplexDataModel

Zostanie wyświetlone ostrzeżenie o możliwej utracie danych.

An operation was scaffolded that may result in the loss of data. Please review the migration for accuracy.
Done. To undo this action, use 'ef migrations remove'

Jeśli próbowano uruchomić database update polecenie w tym momencie (jeszcze tego nie zrobisz), zostanie wyświetlony następujący błąd:

Instrukcja ALTER TABLE powoduje konflikt z ograniczeniem KLUCZ OBCY "FK_dbo. Course_dbo. Department_DepartmentID". Konflikt wystąpił w bazie danych "ContosoUniversity", tabeli "dbo". Dział", kolumna "DepartmentID".

Czasami podczas wykonywania migracji z istniejącymi danymi należy wstawić dane wycinkowe do bazy danych, aby spełnić ograniczenia klucza obcego. Wygenerowany kod w metodzie Up dodaje do Course tabeli niepusty DepartmentID klucz obcy. Jeśli w tabeli Course istnieją już wiersze po uruchomieniu kodu, operacja kończy się niepowodzeniem, AddColumn ponieważ program SQL Server nie wie, jaką wartość należy umieścić w kolumnie, która nie może mieć wartości null. Na potrzeby tego samouczka uruchomisz migrację w nowej bazie danych, ale w aplikacji produkcyjnej trzeba będzie przeprowadzić migrację do istniejących danych, więc w poniższych kierunkach pokazano przykład tego, jak to zrobić.

Aby migracja działała z istniejącymi danymi, musisz zmienić kod, aby nadać nowej kolumnie wartość domyślną, i utworzyć dział wycinków o nazwie "Temp", aby działał jako domyślny dział. W związku z tym istniejące wiersze kursu będą powiązane z działem "Temp" po uruchomieniu Up metody.

  • Otwórz plik {timestamp}_ComplexDataModel.cs.

  • Oznacz jako komentarz wiersz kodu, który dodaje kolumnę DepartmentID do tabeli Course.

    migrationBuilder.AlterColumn<string>(
        name: "Title",
        table: "Course",
        maxLength: 50,
        nullable: true,
        oldClrType: typeof(string),
        oldNullable: true);
                
    //migrationBuilder.AddColumn<int>(
    //    name: "DepartmentID",
    //    table: "Course",
    //    nullable: false,
    //    defaultValue: 0);
    
  • Dodaj następujący wyróżniony kod po kodzie, który tworzy tabelę Dział:

    migrationBuilder.CreateTable(
        name: "Department",
        columns: table => new
        {
            DepartmentID = table.Column<int>(nullable: false)
                .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
            Budget = table.Column<decimal>(type: "money", nullable: false),
            InstructorID = table.Column<int>(nullable: true),
            Name = table.Column<string>(maxLength: 50, nullable: true),
            StartDate = table.Column<DateTime>(nullable: false)
        },
        constraints: table =>
        {
            table.PrimaryKey("PK_Department", x => x.DepartmentID);
            table.ForeignKey(
                name: "FK_Department_Instructor_InstructorID",
                column: x => x.InstructorID,
                principalTable: "Instructor",
                principalColumn: "ID",
                onDelete: ReferentialAction.Restrict);
        });
    
    migrationBuilder.Sql("INSERT INTO dbo.Department (Name, Budget, StartDate) VALUES ('Temp', 0.00, GETDATE())");
    // Default value for FK points to department created above, with
    // defaultValue changed to 1 in following AddColumn statement.
    
    migrationBuilder.AddColumn<int>(
        name: "DepartmentID",
        table: "Course",
        nullable: false,
        defaultValue: 1);
    

W aplikacji produkcyjnej napiszesz kod lub skrypty, aby dodać wiersze działu i powiązać wiersze kursu z nowymi wierszami działu. Nie potrzebujesz już działu "Temp" ani wartości domyślnej w kolumnie Course.DepartmentID .

Zapisz zmiany i skompiluj projekt.

Zmienianie parametry połączenia

Teraz masz nowy kod w klasie, który dodaje dane inicjujące DbInitializer dla nowych jednostek do pustej bazy danych. Aby program EF utworzył nową pustą bazę danych, zmień nazwę bazy danych w parametry połączenia na appsettings.json ContosoUniversity3 lub inną nazwę, której nie użyto na komputerze, którego używasz.

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity3;Trusted_Connection=True;MultipleActiveResultSets=true"
  },

Zapisz zmianę na appsettings.json.

Uwaga

Alternatywą dla zmiany nazwy bazy danych jest usunięcie bazy danych. Użyj programu SQL Server Eksplorator obiektów (SSOX) lub polecenia interfejsu database drop wiersza polecenia:

dotnet ef database drop

Aktualizowanie bazy danych

Po zmianie nazwy bazy danych lub usunięciu bazy danych uruchom database update polecenie w oknie poleceń, aby wykonać migracje.

dotnet ef database update

Uruchom aplikację, aby spowodować DbInitializer.Initialize uruchomienie metody i wypełnienie nowej bazy danych.

Otwórz bazę danych w systemie SSOX, tak jak wcześniej, i rozwiń węzeł Tabele , aby zobaczyć, że wszystkie tabele zostały utworzone. (Jeśli nadal masz otwarte rozwiązanie SSOX z wcześniejszego czasu, kliknij przycisk Przycisk Odśwież ).

Tables in SSOX

Uruchom aplikację, aby wyzwolić kod inicjatora, który wysieje bazę danych.

Kliknij prawym przyciskiem myszy tabelę CourseAssignment i wybierz pozycję Wyświetl dane , aby sprawdzić, czy zawiera ona dane.

CourseAssignment data in SSOX

Uzyskiwanie kodu

Pobierz lub wyświetl ukończoną aplikację.

Następne kroki

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

  • Dostosowany model danych
  • Wprowadzono zmiany w jednostce Student
  • Utworzono jednostkę instruktora
  • Utworzono jednostkę OfficeAssignment
  • Zmodyfikowana jednostka Kursu
  • Utworzona jednostka Działu
  • Zmodyfikowana jednostka rejestracji
  • Zaktualizowano kontekst bazy danych
  • Baza danych rozstawiona z danymi testowymi
  • Dodano migrację
  • Zmieniono parametry połączenia
  • Zaktualizowano bazę danych

Przejdź do następnego samouczka, aby dowiedzieć się więcej na temat uzyskiwania dostępu do powiązanych danych.