Samouczek: tworzenie bardziej złożonego modelu danych dla aplikacji MVC ASP.NET
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 reguły formatowania, walidacji i mapowania bazy danych. W tym artykule przedstawiono dwa sposoby dostosowywania modelu danych: dodając atrybuty do klas jednostek i dodając kod do klasy kontekstu bazy danych.
Po zakończeniu klasy jednostek składają się na ukończony model danych pokazany na poniższej ilustracji:
W tym samouczku zostały wykonane następujące czynności:
- Dostosowywanie modelu danych
- Aktualizowanie jednostki Student
- Tworzenie jednostki instruktora
- Tworzenie jednostki OfficeAssignment
- Modyfikowanie jednostki Course
- Tworzenie jednostki Dział
- Modyfikowanie jednostki Rejestracja
- Dodawanie kodu do kontekstu bazy danych
- Inicjuj bazę danych z danymi testowymi
- Dodawanie migracji
- 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 School
model danych, dodając atrybuty do już utworzonych 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 obszarze Models\Student.cs dodaj instrukcję using
dla System.ComponentModel.DataAnnotations
przestrzeni nazw i dodaj DataType
i DisplayFormat
atrybuty 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 virtual 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, Numer telefonu, 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 elementu DataType.EmailAddress, a selektor dat można podać dla elementu DataType.Date w przeglądarkach, które obsługują kod HTML5. Atrybuty DataType emitują atrybuty HTML 5 data- (wymawiane kreski 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 określone 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).
Atrybut DisplayFormat można użyć samodzielnie, ale zazwyczaj warto użyć 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ą DisplayFormat
polecenia :
- 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.
- Atrybut DataType może umożliwić MVC wybranie odpowiedniego szablonu pola do renderowania danych ( DisplayFormat używa szablonu ciągu). Aby uzyskać więcej informacji, zobacz szablony ASP.NET MVC 2 Brada Wilsona. (Chociaż został napisany dla MVC 2, ten artykuł nadal dotyczy bieżącej wersji ASP.NET MVC).
Jeśli używasz atrybutu DataType
z polem daty, musisz również określić DisplayFormat
atrybut, aby upewnić się, że pole jest poprawnie renderowane w przeglądarkach Chrome. Aby uzyskać więcej informacji, zobacz ten wątek StackOverflow.
Aby uzyskać więcej informacji na temat obsługi innych formatów dat w MVC, przejdź do tematu MVC 5 Introduction: Examining the Edit Methods and Edit View and search in the page for "internationalization" (Wprowadzenie do wzorca MVC: badanie metod edycji i edytowanie widoku oraz wyszukiwanie na stronie "internationalization").
Ponownie uruchom stronę 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 Student
modelu.
Atrybut StringLengthAttribute
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 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 atrybuty StringLength 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, ErrorMessage = "First name cannot be longer than 50 characters.")]
public string FirstMidName { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime EnrollmentDate { get; set; }
public virtual 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ą być alfabetyczne:
[RegularExpression(@"^[A-Z]+[a-zA-Z""'\s-]*$")]
Atrybut MaxLength zapewnia podobne funkcje do atrybutu StringLength , ale nie zapewnia weryfikacji po stronie klienta.
Uruchom aplikację i kliknij kartę Uczniowie . Zostanie wyświetlony następujący błąd:
Model tworzący kopię zapasową kontekstu "SchoolContext" zmienił się od czasu utworzenia bazy danych. Rozważ użycie Migracje Code First do zaktualizowania bazy danych (https://go.microsoft.com/fwlink/?LinkId=238269).
Model bazy danych zmienił się w sposób, który wymaga zmiany schematu bazy danych, a program Entity Framework wykrył to. Za pomocą migracji zaktualizujesz schemat bez utraty danych dodanych do bazy danych przy użyciu interfejsu użytkownika. Jeśli zmieniono dane utworzone przez Seed
metodę, zostaną zmienione z powrotem na jej oryginalny stan ze względu na metodę AddOrUpdate , której używasz w metodzie Seed
. (AddOrUpdate jest odpowiednikiem operacji "upsert" z terminologii bazy danych).
W konsoli Menedżer pakietów (PMC) wprowadź następujące polecenia:
add-migration MaxLengthOnNames
update-database
Polecenie add-migration
tworzy 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 update-database
uruchomiło ten kod.
Sygnatura czasowa poprzedzona nazwą pliku migracji jest używana przez program Entity Framework do zamawiania migracji. Przed uruchomieniem update-database
polecenia można utworzyć wiele migracji, a następnie wszystkie migracje są stosowane w kolejności, w jakiej zostały utworzone.
Uruchom stronę Tworzenie i wprowadź nazwę dłuższą niż 50 znaków. Po kliknięciu przycisku Utwórz weryfikacja po stronie klienta wyświetla komunikat o błędzie: Pole LastName musi być ciągiem o maksymalnej długości 50.
Atrybut kolumny
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.
W pliku Student.cs dodaj instrukcję using
System.ComponentModel.DataAnnotations.Schema i 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, ErrorMessage = "First name cannot be longer than 50 characters.")]
[Column("FirstName")]
public string FirstMidName { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
}
Dodanie atrybutu Kolumna zmienia model kopii zapasowej obiektu SchoolContext, więc nie będzie zgodny z bazą danych. Wprowadź następujące polecenia w usłudze PMC, aby utworzyć inną migrację:
add-migration ColumnFirstName
update-database
W Eksploratorze serwera otwórz projektanta tabeli Student , klikając dwukrotnie tabelę Student .
Na poniższej ilustracji przedstawiono oryginalną nazwę kolumny, tak jak przed zastosowaniem dwóch pierwszych migracji. Oprócz zmiany nazwy kolumny z FirstMidName
na FirstName
kolumna dwie kolumny nazw zmieniły się z MAX
długości na 50 znaków.
Możesz również wprowadzić zmiany mapowania bazy danych przy użyciu interfejsu API Fluent, jak zobaczysz w dalszej części tego samouczka.
Uwaga
Jeśli spróbujesz skompilować przed zakończeniem tworzenia wszystkich klas jednostek w poniższych sekcjach, mogą wystąpić błędy kompilatora.
Aktualizowanie jednostki Student
W obszarze Models\Student.cs 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, ErrorMessage = "First name cannot be longer than 50 characters.")]
[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 virtual ICollection<Enrollment> Enrollments { get; set; }
}
}
Wymagany atrybut
Atrybut Wymagany sprawia, że wymagane pola właściwości nazwy. Parametr Required attribute
nie jest wymagany dla typów wartości, takich jak DateTime, int, double i float. Typy wartości nie mogą być przypisane wartości null, dlatego są one z natury 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; }
MinimumLength
i Required
zezwalaj na sprawdzanie poprawności przez biały znak. Użyj atrybutu , aby RegularExpression
uzyskać pełną kontrolę nad ciągiem.
Atrybut wyświetlania
Atrybut Display
określa, że podpis pól tekstowych powinien mieć wartość "Imię", "Nazwisko", "Imię", "Pełna nazwa" i "Data rejestracji" zamiast nazwy właściwości w każdym wystąpieniu (bez 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ę get
dostępu i żadna kolumna nie FullName
zostanie wygenerowana w bazie danych.
Tworzenie jednostki instruktora
Utwórz modele\Instructor.cs, 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 virtual ICollection<Course> Courses { get; set; }
public virtual OfficeAssignment OfficeAssignment { get; set; }
}
}
Zwróć uwagę, że kilka właściwości jest takich samych w jednostkach Student
i Instructor
. W samouczku Implementowanie dziedziczenia w dalszej części tej serii refaktoryzujesz ten kod, aby wyeliminować nadmiarowość.
Możesz umieścić wiele atrybutów w jednym wierszu, aby można było również napisać klasę instruktora w następujący sposób:
public class Instructor
{
public int ID { get; set; }
[Display(Name = "Last Name"),StringLength(50, MinimumLength=1)]
public string LastName { get; set; }
[Column("FirstName"),Display(Name = "First Name"),StringLength(50, MinimumLength=1)]
public string FirstMidName { get; set; }
[DataType(DataType.Date),Display(Name = "Hire Date"),DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime HireDate { get; set; }
[Display(Name = "Full Name")]
public string FullName
{
get { return LastName + ", " + FirstMidName; }
}
public virtual ICollection<Course> Courses { get; set; }
public virtual OfficeAssignment OfficeAssignment { get; set; }
}
Właściwości nawigacji Kursy i OfficeAssignment
Właściwości Courses
i OfficeAssignment
to właściwości nawigacji. Jak wyjaśniono wcześniej, są one zwykle definiowane jako wirtualne , dzięki czemu mogą korzystać z funkcji Entity Framework nazywanej leniwym ładowaniem. Ponadto jeśli właściwość nawigacji może przechowywać wiele jednostek, jej typ musi zaimplementować interfejs ICollection<T> . Na przykład IList<T> kwalifikuje się, ale nie IEnumerable<T> , ponieważ IEnumerable<T>
nie implementuje funkcji Dodaj.
Instruktor może uczyć dowolną liczbę kursów, dlatego Courses
jest definiowany jako kolekcja Course
jednostek.
public virtual ICollection<Course> Courses { get; set; }
Nasze reguły biznesowe określają, że instruktor może mieć tylko jedno biuro, więc OfficeAssignment
jest definiowany jako jedna OfficeAssignment
jednostka (która może być null
, jeśli nie ma przypisanego biura).
public virtual OfficeAssignment OfficeAssignment { get; set; }
Tworzenie jednostki OfficeAssignment
Utwórz modele\OfficeAssignment.cs przy użyciu następującego kodu:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public class OfficeAssignment
{
[Key]
[ForeignKey("Instructor")]
public int InstructorID { get; set; }
[StringLength(50)]
[Display(Name = "Office Location")]
public string Location { get; set; }
public virtual Instructor Instructor { get; set; }
}
}
Skompiluj projekt, który zapisuje zmiany i sprawdza, czy nie wprowadzono żadnych błędów kopiowania i wklejania, które kompilator może przechwycić.
Atrybut klucza
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]
[ForeignKey("Instructor")]
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.
Atrybut ForeignKey
Jeśli istnieje relacja jeden do zera lub jeden albo relacja jeden do jednego między dwiema jednostkami (na przykład między OfficeAssignment
i Instructor
), program EF nie może ustalić, który koniec relacji jest podmiotem zabezpieczeń i który koniec jest zależny. Relacje jeden do jednego mają właściwość nawigacji referencyjnej w każdej klasie do innej klasy. Atrybut ForeignKey można zastosować do klasy zależnej w celu ustanowienia relacji. Jeśli pominięto atrybut ForeignKey, podczas próby utworzenia migracji wystąpi następujący błąd:
Nie można określić głównego końca skojarzenia między typami "ContosoUniversity.Models.OfficeAssignment" i "ContosoUniversity.Models.Instructor". Główny koniec tego skojarzenia musi być jawnie skonfigurowany przy użyciu płynnego interfejsu API relacji lub adnotacji danych.
W dalszej części samouczka zobaczysz, jak skonfigurować tę relację z płynnym interfejsem API.
Właściwość nawigacji instruktora
Jednostka Instructor
ma właściwość nawigacji dopuszczającej OfficeAssignment
wartość null (ponieważ instruktor może nie mieć przypisania pakietu Office), a OfficeAssignment
jednostka ma właściwość nawigacji bez wartości null Instructor
(ponieważ przypisanie pakietu Office nie może istnieć bez instruktora — InstructorID
jest niepuste). Instructor
Gdy jednostka ma powiązaną OfficeAssignment
jednostkę, każda jednostka będzie mieć odwołanie do drugiej jednostki we 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ż klucz obcy InstructorID (który jest również kluczem do tej tabeli) nie może mieć wartości null.
Modyfikowanie jednostki Course
W obszarze Models\Course.cs zastąp kod dodany wcześniej następującym kodem:
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 virtual Department Department { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
public virtual ICollection<Instructor> Instructors { get; set; }
}
}
Jednostka kursu ma właściwość DepartmentID
klucza obcego, która wskazuje powiązaną Department
Department
jednostkę 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 wszędzie tam, gdzie są potrzebne. Jednak posiadanie klucza obcego w modelu danych może sprawić, że aktualizacje będą prostsze i bardziej wydajne. Na przykład podczas pobierania jednostki kursu do edycji jednostka ma wartość null, Department
jeśli nie zostanie załadowana, więc podczas aktualizowania jednostki kursu 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 we CourseID
właściwości 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.
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 iDepartment
właściwość nawigacji z powodów wymienionych powyżej.public int DepartmentID { get; set; } public virtual 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 virtual ICollection<Enrollment> Enrollments { get; set; }
Kurs może być nauczany przez wielu instruktorów, więc
Instructors
właściwość nawigacji jest kolekcją:public virtual ICollection<Instructor> Instructors { get; set; }
Tworzenie jednostki Dział
Utwórz modele\Department.cs przy użyciu 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 virtual Instructor Administrator { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
}
Atrybut kolumny
Wcześniej użyto atrybutu Kolumna 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 pieniędzy programu SQL Server w bazie danych:
[Column(TypeName="money")]
public decimal Budget { get; set; }
Mapowanie kolumn nie jest zwykle wymagane, ponieważ program Entity Framework zwykle 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. Aby uzyskać więcej informacji o typach danych CLR i sposobie ich dopasowania do typów danych programu SQL Server, zobacz SqlClient for Entity FrameworkTypes.
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 doInstructor
jednostki, a znak zapytania jest dodawany point
oznaczeniu typu, aby oznaczyć właściwość jako dopuszczaną wartość null. Właściwość nawigacji ma nazwęAdministrator
, ale zawieraInstructor
jednostkę:public int? InstructorID { get; set; } public virtual Instructor Administrator { get; set; }
Dział może mieć wiele kursów, więc istnieje
Courses
właściwość nawigacji:public virtual 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 nie zdefiniowano
Department.InstructorID
właściwości jako dopuszczającą wartość null, zostanie wyświetlony następujący komunikat o wyjątku: "Relacja referencyjna spowoduje cykliczne odwołanie, które jest niedozwolone". Jeśli reguły biznesowe wymagałyInstructorID
, aby właściwość nie mogła mieć wartości null, należy użyć następującej płynnej instrukcji interfejsu API, aby wyłączyć usuwanie kaskadowe w relacji:
modelBuilder.Entity().HasRequired(d => d.Administrator).WithMany().WillCascadeOnDelete(false);
Modyfikowanie jednostki Rejestracja
W obszarze Models\Enrollment.cs zastąp kod dodany wcześniej następującym kodem
using System.ComponentModel.DataAnnotations;
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 virtual Course Course { get; set; }
public virtual 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 virtual 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 virtual 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. Oznacza to, ż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 elementu Narzędzia Entity Framework Power Tools; tworzenie diagramu nie jest częścią tego samouczka— jest ono używane tylko tutaj jako ilustracja).
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 będzie ona odpowiadać tabeli sprzężenia wiele-do-wielu bez ładunku (lub czystej tabeli sprzężenia) w bazie danych i nie trzeba by w ogóle tworzyć dla niej klasy modelu. Jednostki Instructor
i Course
mają taką relację wiele-do-wielu, a jak widać, między nimi nie ma żadnej klasy jednostek:
Tabela sprzężenia jest jednak wymagana w bazie danych, jak pokazano na poniższym diagramie bazy danych:
Program Entity Framework automatycznie tworzy tabelę CourseInstructor
i odczytaj ją i zaktualizujesz pośrednio, odczytując i aktualizując Instructor.Courses
właściwości nawigacji i Course.Instructors
.
Diagram relacji między encjami
Na poniższej ilustracji przedstawiono diagram utworzony przez narzędzia Entity Framework Power Tools dla ukończonego modelu School.
Oprócz linii relacji wiele-do-wielu (* do *) i wierszy 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ł.
Dodawanie kodu do kontekstu bazy danych
Następnie dodasz nowe jednostki do SchoolContext
klasy i dostosujesz niektóre mapowania przy użyciu płynnych wywołań interfejsu API . Interfejs API jest "płynny", ponieważ jest często używany przez ciągowanie serii wywołań metod w jedną instrukcję, jak w poniższym przykładzie:
modelBuilder.Entity<Course>()
.HasMany(c => c.Instructors).WithMany(i => i.Courses)
.Map(t => t.MapLeftKey("CourseID")
.MapRightKey("InstructorID")
.ToTable("CourseInstructor"));
W tym samouczku użyjesz 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.
Aby dodać nowe jednostki do modelu danych i wykonać mapowanie bazy danych, które nie zostało wykonane przy użyciu atrybutów, zastąp kod w dal\SchoolContext.cs następującym kodem:
using ContosoUniversity.Models;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
namespace ContosoUniversity.DAL
{
public class SchoolContext : DbContext
{
public DbSet<Course> Courses { get; set; }
public DbSet<Department> Departments { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Instructor> Instructors { get; set; }
public DbSet<Student> Students { get; set; }
public DbSet<OfficeAssignment> OfficeAssignments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Course>()
.HasMany(c => c.Instructors).WithMany(i => i.Courses)
.Map(t => t.MapLeftKey("CourseID")
.MapRightKey("InstructorID")
.ToTable("CourseInstructor"));
}
}
}
Nowa instrukcja w metodzie OnModelCreating konfiguruje tabelę sprzężenia wiele do wielu:
W przypadku relacji wiele-do-wielu między jednostkami
Instructor
iCourse
kod określa nazwy tabel i kolumn dla tabeli sprzężenia. Code First może skonfigurować relację wiele-do-wielu bez tego kodu, ale jeśli go nie wywołasz, otrzymasz nazwy domyślne, takie jakInstructorInstructorID
dlaInstructorID
kolumny.modelBuilder.Entity<Course>() .HasMany(c => c.Instructors).WithMany(i => i.Courses) .Map(t => t.MapLeftKey("CourseID") .MapRightKey("InstructorID") .ToTable("CourseInstructor"));
Poniższy kod zawiera przykład użycia płynnego interfejsu API zamiast atrybutów w celu określenia relacji między jednostkami Instructor
i OfficeAssignment
:
modelBuilder.Entity<Instructor>()
.HasOptional(p => p.OfficeAssignment).WithRequired(p => p.Instructor);
Aby uzyskać informacje o tym, jakie instrukcje "fluent API" działają w tle, zobacz wpis w blogu Fluent API .
Inicjuj bazę danych z danymi testowymi
Zastąp kod w pliku Migrations\Configuration.cs następującym kodem, aby udostępnić dane inicjowania dla nowo utworzonych jednostek.
namespace ContosoUniversity.Migrations
{
using ContosoUniversity.Models;
using ContosoUniversity.DAL;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;
internal sealed class Configuration : DbMigrationsConfiguration<SchoolContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(SchoolContext context)
{
var students = new List<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") }
};
students.ForEach(s => context.Students.AddOrUpdate(p => p.LastName, s));
context.SaveChanges();
var instructors = new List<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") }
};
instructors.ForEach(s => context.Instructors.AddOrUpdate(p => p.LastName, s));
context.SaveChanges();
var departments = new List<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 }
};
departments.ForEach(s => context.Departments.AddOrUpdate(p => p.Name, s));
context.SaveChanges();
var courses = new List<Course>
{
new Course {CourseID = 1050, Title = "Chemistry", Credits = 3,
DepartmentID = departments.Single( s => s.Name == "Engineering").DepartmentID,
Instructors = new List<Instructor>()
},
new Course {CourseID = 4022, Title = "Microeconomics", Credits = 3,
DepartmentID = departments.Single( s => s.Name == "Economics").DepartmentID,
Instructors = new List<Instructor>()
},
new Course {CourseID = 4041, Title = "Macroeconomics", Credits = 3,
DepartmentID = departments.Single( s => s.Name == "Economics").DepartmentID,
Instructors = new List<Instructor>()
},
new Course {CourseID = 1045, Title = "Calculus", Credits = 4,
DepartmentID = departments.Single( s => s.Name == "Mathematics").DepartmentID,
Instructors = new List<Instructor>()
},
new Course {CourseID = 3141, Title = "Trigonometry", Credits = 4,
DepartmentID = departments.Single( s => s.Name == "Mathematics").DepartmentID,
Instructors = new List<Instructor>()
},
new Course {CourseID = 2021, Title = "Composition", Credits = 3,
DepartmentID = departments.Single( s => s.Name == "English").DepartmentID,
Instructors = new List<Instructor>()
},
new Course {CourseID = 2042, Title = "Literature", Credits = 4,
DepartmentID = departments.Single( s => s.Name == "English").DepartmentID,
Instructors = new List<Instructor>()
},
};
courses.ForEach(s => context.Courses.AddOrUpdate(p => p.CourseID, s));
context.SaveChanges();
var officeAssignments = new List<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" },
};
officeAssignments.ForEach(s => context.OfficeAssignments.AddOrUpdate(p => p.InstructorID, s));
context.SaveChanges();
AddOrUpdateInstructor(context, "Chemistry", "Kapoor");
AddOrUpdateInstructor(context, "Chemistry", "Harui");
AddOrUpdateInstructor(context, "Microeconomics", "Zheng");
AddOrUpdateInstructor(context, "Macroeconomics", "Zheng");
AddOrUpdateInstructor(context, "Calculus", "Fakhouri");
AddOrUpdateInstructor(context, "Trigonometry", "Harui");
AddOrUpdateInstructor(context, "Composition", "Abercrombie");
AddOrUpdateInstructor(context, "Literature", "Abercrombie");
context.SaveChanges();
var enrollments = new List<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();
}
void AddOrUpdateInstructor(SchoolContext context, string courseTitle, string instructorName)
{
var crs = context.Courses.SingleOrDefault(c => c.Title == courseTitle);
var inst = crs.Instructors.SingleOrDefault(i => i.LastName == instructorName);
if (inst == null)
crs.Instructors.Add(context.Instructors.Single(i => i.LastName == instructorName));
}
}
}
Jak pokazano w pierwszym samouczku, większość tego kodu po prostu aktualizuje lub tworzy nowe obiekty jednostki i ładuje przykładowe dane do właściwości zgodnie z wymaganiami dotyczącymi testowania. Zauważ jednak, że Course
jednostka, która ma relację wiele do wielu z jednostką Instructor
, jest obsługiwana:
var courses = new List<Course>
{
new Course {CourseID = 1050, Title = "Chemistry", Credits = 3,
DepartmentID = departments.Single( s => s.Name == "Engineering").DepartmentID,
Instructors = new List<Instructor>()
},
...
};
courses.ForEach(s => context.Courses.AddOrUpdate(p => p.CourseID, s));
context.SaveChanges();
Podczas tworzenia Course
obiektu należy zainicjować Instructors
właściwość nawigacji jako pustą kolekcję przy użyciu kodu Instructors = new List<Instructor>()
. Dzięki temu można dodawać Instructor
jednostki powiązane z tym Course
za pomocą Instructors.Add
metody . Jeśli nie utworzono pustej listy, nie można dodać tych relacji, ponieważ Instructors
właściwość będzie mieć wartość null i nie będzie miała Add
metody. Można również dodać inicjowanie listy do konstruktora.
Dodawanie migracji
W kontrolerze PMC wprowadź add-migration
polecenie (jeszcze nie wykonaj update-database
polecenia):
add-Migration ComplexDataModel
Jeśli próbowano uruchomić update-database
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 i to właśnie teraz trzeba zrobić. Wygenerowany kod w metodzie ComplexDataModel Up
dodaje do Course
tabeli niepusty DepartmentID
klucz obcy. Ponieważ w tabeli są już wiersze po uruchomieniu Course
kodu, operacja zakoń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. W związku z tym należy 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 Course
wiersze będą powiązane z działem "Temp" po uruchomieniu Up
metody. Można je powiązać z właściwymi działami w metodzie Seed
.
<Edytuj plik znacznika> czasu_ComplexDataModel.cs oznacz jako komentarz wiersz kodu, który dodaje kolumnę DepartmentID do tabeli Course i dodaj następujący wyróżniony kod (wyróżniony również wiersz komentarza):
CreateTable(
"dbo.CourseInstructor",
c => new
{
CourseID = c.Int(nullable: false),
InstructorID = c.Int(nullable: false),
})
.PrimaryKey(t => new { t.CourseID, t.InstructorID })
.ForeignKey("dbo.Course", t => t.CourseID, cascadeDelete: true)
.ForeignKey("dbo.Instructor", t => t.InstructorID, cascadeDelete: true)
.Index(t => t.CourseID)
.Index(t => t.InstructorID);
// Create a department for course to point to.
Sql("INSERT INTO dbo.Department (Name, Budget, StartDate) VALUES ('Temp', 0.00, GETDATE())");
// default value for FK points to department created above.
AddColumn("dbo.Course", "DepartmentID", c => c.Int(nullable: false, defaultValue: 1));
//AddColumn("dbo.Course", "DepartmentID", c => c.Int(nullable: false));
AlterColumn("dbo.Course", "Title", c => c.String(maxLength: 50));
Gdy metoda zostanie uruchomiona Seed
, wstawi wiersze w Department
tabeli i będzie odnosić istniejące Course
wiersze do tych nowych Department
wierszy. Jeśli nie dodano żadnych kursów w interfejsie użytkownika, nie potrzebujesz już działu "Temp" ani wartości domyślnej w kolumnie Course.DepartmentID
. Aby zezwolić na możliwość dodania kursów przez kogoś przy użyciu aplikacji, należy również zaktualizować Seed
kod metody, aby upewnić się, że wszystkie Course
wiersze (nie tylko te wstawione przez wcześniejsze uruchomienia Seed
metody) mają prawidłowe DepartmentID
wartości przed usunięciem wartości domyślnej z kolumny i usunięciem działu "Temp".
Aktualizowanie bazy danych
Po zakończeniu edytowania znacznika <czasu>_ComplexDataModel.cs pliku wprowadź update-database
polecenie w pmC, aby wykonać migrację.
update-database
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 zmienić nazwę bazy danych w parametry połączenia lub usunąć bazę danych. Najprostszym podejściem jest zmiana nazwy bazy danych w pliku Web.config . W poniższym przykładzie przedstawiono nazwę zmienioną na CU_Test:
<add name="SchoolContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=CU_Test;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 się nie powiedzie, kolejną czynnością, którą można spróbować, jest ponowne zainicjowanie bazy danych przez wprowadzenie następującego polecenia w usłudze PMC:
update-database -TargetMigration:0
Otwórz bazę danych w Eksploratorze serwera, tak jak wcześniej, i rozwiń węzeł Tabele , aby zobaczyć, że wszystkie tabele zostały utworzone. (Jeśli nadal masz Eksplorator serwera otwarty we wcześniejszej chwili, kliknij przycisk Odśwież .
Nie utworzono klasy modelu dla CourseInstructor
tabeli. Jak wyjaśniono wcześniej, jest to tabela sprzężenia dla relacji wiele-do-wielu między jednostkami Instructor
i Course
.
Kliknij prawym przyciskiem myszy tabelę CourseInstructor
i wybierz polecenie Pokaż dane tabeli, aby sprawdzić, czy zawiera on dane w wyniku jednostek dodanych Instructor
do Course.Instructors
właściwości nawigacji.
Uzyskiwanie kodu
Pobieranie ukończonego projektu
Dodatkowe zasoby
Linki do innych zasobów programu Entity Framework można znaleźć w ASP.NET Dostęp do danych — zalecane zasoby.
Następne kroki
W tym samouczku zostały wykonane następujące czynności:
- Dostosowywanie modelu danych
- Zaktualizowana jednostka Student
- Utworzono jednostkę instruktora
- Utworzono jednostkę OfficeAssignment
- Zmodyfikowano jednostkę Course
- Utworzono jednostkę Dział
- Zmodyfikowano jednostkę Enrollment
- Dodano kod do kontekstu bazy danych
- Baza danych rozstawiona z danymi testowymi
- Dodano migrację
- Zaktualizowano bazę danych
Przejdź do następnego artykułu, aby dowiedzieć się, jak odczytywać i wyświetlać powiązane dane ładowane przez program Entity Framework do właściwości nawigacji.