Sdílet prostřednictvím


Kurz: Vytvoření složitějšího datového modelu pro aplikaci ASP.NET MVC

V předchozích kurzech jste pracovali s jednoduchým datovým modelem složeným ze tří entit. V tomto kurzu přidáte další entity a relace a upravíte datový model zadáním formátování, ověření a pravidel mapování databáze. Tento článek ukazuje dva způsoby přizpůsobení datového modelu: přidáním atributů do tříd entit a přidáním kódu do třídy kontextu databáze.

Po dokončení tvoří třídy entit dokončený datový model, který je zobrazený na následujícím obrázku:

School_class_diagram

V tomto kurzu se naučíte:

  • Přizpůsobení datového modelu
  • Aktualizace entity Student
  • Vytvoření entity Instruktor
  • Vytvoření entity OfficeAssignment
  • Úprava entity Course
  • Vytvoření entity Oddělení
  • Úprava entity registrace
  • Přidání kódu do kontextu databáze
  • Počáteční databáze s testovacími daty
  • Přidání migrace
  • Aktualizace databáze

Požadavky

Přizpůsobení datového modelu

V této části se dozvíte, jak přizpůsobit datový model pomocí atributů, které určují formátování, ověřování a pravidla mapování databáze. Potom v několika následujících částech vytvoříte kompletní School datový model přidáním atributů do tříd, které jste už vytvořili, a vytvořením nových tříd pro zbývající typy entit v modelu.

Atribut DataType

U dat zápisu studentů se na všech webových stránkách aktuálně zobrazuje čas spolu s datem, i když všechno, co vás zajímá o toto pole, je datum. Pomocí atributů datových poznámek můžete provést jednu změnu kódu, která opraví formát zobrazení v každém zobrazení, které zobrazuje data. Pokud chcete vidět příklad toho, jak to udělat, přidáte do vlastnosti ve Student třídě atributEnrollmentDate.

V models\Student.cs přidejte using příkaz pro System.ComponentModel.DataAnnotations obor názvů a přidejte DataType a DisplayFormat atributy do EnrollmentDate vlastnosti, jak je znázorněno v následujícím příkladu:

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

Atribut DataType slouží k určení datového typu, který je konkrétnější než vnitřní typ databáze. V tomto případě chceme sledovat pouze datum, nikoli datum a čas. Výčet DataType poskytuje mnoho datových typů, jako je datum, čas, telefonní číslo, měna, EmailAddress a další. Atribut DataType může také aplikaci povolit, aby automaticky poskytovala funkce specifické pro typ. Odkaz lze například mailto: vytvořit pro DataType.EmailAddress a pro DataType.Date lze v prohlížečích podporujících HTML5 zadat selektor data. Atributy DataType generují atributy HTML 5 data- (vyslovuje se pomlčka), které můžou prohlížeče HTML 5 pochopit. Atributy DataType neposkytují žádné ověření.

DataType.Date nezadá formát zobrazeného data. Ve výchozím nastavení se datové pole zobrazí podle výchozích formátů založených na souboru CultureInfo serveru.

Atribut DisplayFormat se používá k explicitní zadání formátu data:

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

Nastavení ApplyFormatInEditMode určuje, že zadané formátování se má použít také při zobrazení hodnoty v textovém poli pro úpravy. (U některých polí to možná nechcete – například pro hodnoty měny, nemusíte chtít symbol měny v textovém poli pro úpravy.)

Atribut DisplayFormat můžete použít samostatně, ale obecně je vhodné použít také atribut DataType. Atribut DataType vyjadřuje sémantiku dat na rozdíl od toho, jak je vykreslit na obrazovce, a poskytuje následující výhody, se DisplayFormatkterými se nedostanete:

  • Prohlížeč může povolit funkce HTML5 (například zobrazení ovládacího prvku kalendáře, symbol měny odpovídající národnímu prostředí, e-mailové odkazy, ověření vstupu na straně klienta atd.).
  • Ve výchozím nastavení prohlížeč vykresluje data pomocí správného formátu na základě vašeho národního prostředí.
  • Atribut DataType umožňuje MVC zvolit správnou šablonu pole pro vykreslení dat ( DisplayFormat používá šablonu řetězce). Další informace najdete v tématu Brad Wilson's ASP.NET MVC 2 Templates. (I když je napsaný pro MVC 2, tento článek se stále vztahuje na aktuální verzi ASP.NET MVC.)

Pokud atribut používáte DataType s polem kalendářního data, musíte atribut zadat DisplayFormat také, aby se pole vykreslovaly správně v prohlížečích Chrome. Další informace najdete v tomto vlákně StackOverflow.

Další informace o tom, jak zpracovat jiné formáty kalendářních dat v MVC, přejděte do MVC 5 Úvod: Zkoumání metod úprav a zobrazení pro úpravy a hledání na stránce "internationalization".

Znovu spusťte stránku Index studenta a všimněte si, že časy se už nezobrazují pro data registrace. Totéž platí pro každé zobrazení, které model používá Student .

Students_index_page_with_formatted_date

The StringLengthAttribute

Pomocí atributů můžete také zadat pravidla ověření dat a chybové zprávy ověření. Atribut StringLength nastaví maximální délku v databázi a poskytuje ověřování na straně klienta a na straně serveru pro ASP.NET MVC. Můžete také zadat minimální délku řetězce v tomto atributu, ale minimální hodnota nemá žádný vliv na schéma databáze.

Předpokládejme, že chcete zajistit, aby uživatelé nezadáli více než 50 znaků pro jméno. Chcete-li přidat toto omezení, přidejte stringLength atributy do LastName a FirstMidName vlastnosti, jak je znázorněno v následujícím příkladu:

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

Atribut StringLength nezabrání uživateli v zadávání prázdných znaků pro jméno. Atribut RegularExpression můžete použít k použití omezení pro vstup. Například následující kód vyžaduje, aby první znak byl velkým písmenem a zbývající znaky byly abecední:

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

Atribut MaxLength poskytuje podobné funkce jako StringLength atribut, ale neposkytuje ověřování na straně klienta.

Spusťte aplikaci a klikněte na kartu Studenti . Zobrazí se následující chyba:

Model, který zálohuje kontext SchoolContext, se od vytvoření databáze změnil. Zvažte použití Migrace Code First k aktualizaci databáze (https://go.microsoft.com/fwlink/?LinkId=238269).

Databázový model se změnil způsobem, který vyžaduje změnu ve schématu databáze, a Entity Framework to zjistil. Migrace použijete k aktualizaci schématu bez ztráty dat přidaných do databáze pomocí uživatelského rozhraní. Pokud jste změnili data vytvořená Seed metodou, která se změní zpět do původního stavu kvůli metodě AddOrUpdate , kterou v Seed metodě používáte. (AddOrUpdate je ekvivalentní operaci upsert z terminologie databáze.)

V konzole Správce balíčků (PMC) zadejte následující příkazy:

add-migration MaxLengthOnNames
update-database

Příkaz add-migration vytvoří soubor s názvem <timeStamp>_MaxLengthOnNames.cs. Tento soubor obsahuje kód v Up metodě, která aktualizuje databázi tak, aby odpovídala aktuálnímu datovému modelu. Příkaz update-database spustil tento kód.

Rozhraní Entity Framework používá časové razítko před názvem souboru migrace k řazení migrací. Před spuštěním update-database příkazu můžete vytvořit několik migrací a potom se všechny migrace použijí v pořadí, v jakém byly vytvořeny.

Spusťte stránku Vytvořit a zadejte název delší než 50 znaků. Když kliknete na Vytvořit, zobrazí se na straně klienta chybová zpráva: Pole LastName musí být řetězec s maximální délkou 50.

Atribut sloupce

Pomocí atributů můžete také řídit, jak se třídy a vlastnosti mapují na databázi. Předpokládejme, že jste použili název FirstMidName pole pro jméno, protože pole může obsahovat také prostřední název. Ale chcete, aby byl databázový sloupec pojmenován FirstName, protože uživatelé, kteří budou psát ad hoc dotazy na databázi, jsou zvyklí na tento název. K vytvoření tohoto mapování můžete použít Column atribut.

Atribut Column určuje, že při vytvoření databáze bude sloupec Student tabulky, která se mapuje na FirstMidName vlastnost, pojmenována FirstName. Jinými slovy, když váš kód odkazuje Student.FirstMidName, data pocházejí nebo se aktualizují ve FirstName sloupci Student tabulky. Pokud nezadáte názvy sloupců, mají stejný název jako název vlastnosti.

V souboru Student.cs přidejte using příkaz pro System.ComponentModel.DataAnnotations.Schema a přidejte atribut názvu sloupce do FirstMidName vlastnosti, jak je znázorněno v následujícím zvýrazněném kódu:

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

Přidání atributu Column změní model backing SchoolContext, takže se neshoduje s databází. Zadáním následujících příkazů v PMC vytvořte další migraci:

add-migration ColumnFirstName
update-database

V Průzkumníku serveru otevřete návrháře tabulky Student poklikáním na tabulku Student.

Následující obrázek znázorňuje původní název sloupce, který byl před prvními dvěma migracemi. Kromě názvu sloupce, ze FirstMidName který se mění na FirstName, se dva sloupce názvů změnily z MAX délky na 50 znaků.

Dva snímky obrazovky, které znázorňují rozdíly v tabulce Jméno a Datový typ dvou tabulek Student.

Změny mapování databáze můžete provádět také pomocí rozhraní Fluent API, jak uvidíte později v tomto kurzu.

Poznámka:

Pokud se pokusíte zkompilovat před dokončením vytváření všech tříd entit v následujících částech, může dojít k chybám kompilátoru.

Aktualizace entity Student

V models\Student.cs nahraďte kód, který jste přidali dříve, následujícím kódem. Změny jsou zvýrazněné.

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

Požadovaný atribut

Atribut Required vytvoří požadované vlastnosti názvu pole. Pro Required attribute typy hodnot, jako jsou DateTime, int, double a float, není potřeba. Typy hodnot nelze přiřadit hodnotu null, takže jsou ze své podstaty považovány za povinná pole.

Atribut Required musí být použit s vynuceným vynuceným atributem MinimumLength MinimumLength .

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

MinimumLength a Required povolte, aby ověření splňovalo prázdné znaky. RegularExpression Použijte atribut pro úplnou kontrolu nad řetězcem.

Atribut zobrazení

Atribut Display určuje, že titulek textových polí by měl být "Jméno", "Příjmení", "Příjmení", "Celé jméno" a "Datum registrace" místo názvu vlastnosti v každé instanci (který neobsahuje mezeru rozdělující slova).

Počítaná vlastnost FullName

FullName je počítaná vlastnost, která vrací hodnotu vytvořenou zřetězením dvou dalších vlastností. Proto má pouze přístupové get objekty a v databázi se nevygeneruje žádný FullName sloupec.

Vytvoření entity Instruktor

Vytvořte modely\Instructor.cs a nahraďte kód šablony následujícím kódem:

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

Všimněte si, že několik vlastností je v entitách Student Instructor stejné. V kurzu Implementace dědičnosti dále v této sérii refaktorujete tento kód, abyste eliminovali redundanci.

Na jeden řádek můžete umístit více atributů, takže můžete také napsat třídu instruktora následujícím způsobem:

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

Vlastnosti navigace Courses a OfficeAssignment

Vlastnosti Courses a OfficeAssignment vlastnosti jsou navigační vlastnosti. Jak bylo vysvětleno dříve, jsou obvykle definovány jako virtuální , aby mohly využívat funkci Entity Framework označovanou jako opožděné načítání. Kromě toho, pokud navigační vlastnost může obsahovat více entit, jeho typ musí implementovat ICollection<T> Rozhraní. Například IList<T> kvalifikuje, ale ne IEnumerable<T>, protože IEnumerable<T> neimplementuje Add.

Instruktor může učit libovolný počet kurzů, takže Courses je definován jako kolekce Course entit.

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

Naše obchodní pravidla uvádějí, že instruktor může mít maximálně jednu kancelář, takže OfficeAssignment je definována jako jedna OfficeAssignment entita (což může být v null případě, že není přiřazena žádná kancelář).

public virtual OfficeAssignment OfficeAssignment { get; set; }

Vytvoření entity OfficeAssignment

Vytvořte modely\OfficeAssignment.cs s následujícím kódem:

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

Sestavte projekt, který uloží změny a ověří, že jste neprovedli žádné kopírování a vkládání chyb, které kompilátor dokáže zachytit.

Klíčový atribut

Mezi entitami OfficeAssignment existuje relace Instructor 1:0 nebo 1. Přiřazení kanceláře existuje pouze ve vztahu k instruktorovi, kterému je přiřazen, a proto je jeho primárním klíčem i jeho cizí klíč k entitě Instructor . Entity Framework ale nedokáže automaticky rozpoznat InstructorID jako primární klíč této entity, protože jeho název neodpovídá ID konvenci pojmenování názvů nebo názvů třídID. Key Atribut se proto používá k identifikaci jako klíče:

[Key]
[ForeignKey("Instructor")]
public int InstructorID { get; set; }

Atribut můžete použít Key také v případě, že entita má svůj vlastní primární klíč, ale chcete pojmenovat vlastnost něco jiného než classnameID nebo ID. Ef ve výchozím nastavení považuje klíč za negenerovaný databází, protože sloupec slouží k identifikaci relace.

Atribut ForeignKey

Pokud je mezi dvěma entitami relace 1:1 nebo 1:1 relace mezi dvěma entitami (například mezi OfficeAssignment a Instructor), ef nemůže zjistit, který konec relace je objekt zabezpečení a který konec je závislý. Relace 1:1 mají v každé třídě odkazovou navigační vlastnost do druhé třídy. Atribut ForeignKey lze použít u závislé třídy k navázání relace. Pokud vynecháte atribut ForeignKey, při pokusu o vytvoření migrace se zobrazí následující chyba:

Nelze určit hlavní konec přidružení mezi typy ContosoUniversity.Models.OfficeAssignment a ContosoUniversity.Models.Instructor. Hlavní konec tohoto přidružení musí být explicitně nakonfigurovaný pomocí rozhraní API pro fluent relace nebo datových poznámek.

Později v tomto kurzu se dozvíte, jak nakonfigurovat tento vztah s rozhraním FLUENT API.

Vlastnost navigace instruktora

Entita Instructor má vlastnost navigace s možnou OfficeAssignment hodnotou null (protože instruktor nemusí mít přiřazení kanceláře) a OfficeAssignment entita má nenulovou navigační vlastnost (protože přiřazení kanceláře nemůže existovat bez instruktora – InstructorID je nenulovéInstructor). Pokud má entita Instructor související OfficeAssignment entitu, každá entita bude mít ve své navigační vlastnosti odkaz na druhou entitu.

Do vlastnosti navigace instruktora můžete zadat [Required] atribut, který určuje, že musí existovat související instruktor, ale nemusíte to udělat, protože cizí klíč InstructorID (což je také klíč k této tabulce) není nullable.

Úprava entity Course

V části Models\Course.cs nahraďte dříve přidaný kód následujícím kódem:

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

Entita kurzu má vlastnost DepartmentID cizího klíče, která odkazuje na související Department entitu Department a má navigační vlastnost. Entity Framework nevyžaduje, abyste do datového modelu přidali vlastnost cizího klíče, pokud máte navigační vlastnost pro související entitu. SYSTÉM EF automaticky vytvoří cizí klíče v databázi všude, kde jsou potřeba. Cizí klíč v datovém modelu ale může zjednodušit a zefektivnit aktualizace. Pokud například načtete entitu kurzu, která se má upravit, má entita hodnotu null, Department pokud ji nenačtete, takže když entitu kurzu aktualizujete, musíte nejprve entitu Department načíst. Pokud je vlastnost DepartmentID cizího klíče součástí datového modelu, nemusíte před aktualizací načíst entitu Department .

Atribut DatabaseGenerated

Atribut DatabaseGenerated s parametrem None ve CourseID vlastnosti určuje, že hodnoty primárního klíče jsou poskytovány uživatelem místo generování databáze.

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

Ve výchozím nastavení entity Framework předpokládá, že databáze generuje hodnoty primárního klíče. To je to, co chcete ve většině scénářů. U Course entit ale použijete uživatelem zadané číslo kurzu, například 1000 řad pro jedno oddělení, 2000 řad pro jiné oddělení atd.

Vlastnosti cizího klíče a navigace

Vlastnosti cizího klíče a vlastnosti navigace v entitě Course odrážejí následující relace:

  • Kurz je přiřazen k jednomu oddělení, takže z výše uvedených důvodů existuje DepartmentID cizí klíč a Department vlastnost navigace.

    public int DepartmentID { get; set; }
    public virtual Department Department { get; set; }
    
  • Kurz může mít v něm zaregistrovaný libovolný počet studentů, takže Enrollments navigační vlastnost je kolekce:

    public virtual ICollection<Enrollment> Enrollments { get; set; }
    
  • Kurz může učit více instruktorů, takže Instructors navigační vlastnost je kolekce:

    public virtual ICollection<Instructor> Instructors { get; set; }
    

Vytvoření entity Oddělení

Vytvořte modely\Department.cs s následujícím kódem:

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

Atribut sloupce

Dříve jste použili atribut Sloupec ke změně mapování názvů sloupců. V kódu entity Column se atribut používá ke změně mapování datového Department typu SQL tak, aby se sloupec definoval pomocí typu peněz SQL Serveru v databázi:

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

Mapování sloupců se obvykle nevyžaduje, protože Entity Framework obvykle vybírá příslušný datový typ SQL Serveru na základě typu CLR, který definujete pro vlastnost. Typ CLR decimal se mapuje na typ SQL Serveru decimal . V tomto případě ale víte, že sloupec bude obsahovat částky měny a datový typ peněz je vhodnější pro tento typ. Další informace o datových typech CLR a o tom, jak odpovídají datovým typům SQL Serveru, najdete v tématu SqlClient pro Entity FrameworkTypes.

Vlastnosti cizího klíče a navigace

Vlastnosti cizího klíče a navigace odrážejí následující relace:

  • Oddělení může nebo nemusí mít správce a správce je vždy instruktorem. InstructorID Proto je vlastnost zahrnuta jako cizí klíč entity Instructor a za označení typu se přidá int otazník, který vlastnost označí jako nullable. Navigační vlastnost je pojmenovanáAdministrator, ale obsahuje entituInstructor:

    public int? InstructorID { get; set; }
    public virtual Instructor Administrator { get; set; }
    
  • Oddělení může mít mnoho kurzů, takže je Courses k dispozici navigační vlastnost:

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

    Poznámka:

    Podle konvence umožňuje Entity Framework kaskádové odstranění pro cizí klíče bez hodnoty null a pro relace M:N. Výsledkem může být cyklický kaskádový odstranění pravidel, která při pokusu o přidání migrace způsobí výjimku. Pokud jste například vlastnost nedefinovali jako možnou Department.InstructorID hodnotu null, zobrazila by se následující zpráva o výjimce: "Odkazovací relace bude mít za následek cyklický odkaz, který není povolený." Pokud vaše obchodní pravidla vyžadují InstructorID , aby vlastnost neměla hodnotu null, museli byste k zakázání kaskádového odstranění v relaci použít následující příkaz rozhraní API fluent:

modelBuilder.Entity().HasRequired(d => d.Administrator).WithMany().WillCascadeOnDelete(false);

Úprava entity registrace

V models\Enrollment.cs nahraďte kód, který jste přidali dříve, následujícím kódem.

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

Vlastnosti cizího klíče a navigace

Vlastnosti cizího klíče a vlastnosti navigace odrážejí následující relace:

  • Záznam registrace je určený pro jeden kurz, takže vlastnost cizího CourseID klíče a Course navigační vlastnost:

    public int CourseID { get; set; }
    public virtual Course Course { get; set; }
    
  • Záznam registrace je určený pro jednoho studenta, takže vlastnost cizího StudentID klíče a Student navigační vlastnost:

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

Relace M:N

Mezi entitami a entitami existuje relace Student Course M:N a Enrollment entita funguje jako tabulka spojení M:N s datovou částí v databázi. To znamená, že Enrollment tabulka obsahuje další data kromě cizích klíčů pro spojené tabulky (v tomto případě primární klíč a Grade vlastnost).

Následující obrázek znázorňuje, jak tyto relace vypadají v diagramu entit. (Tento diagram byl vygenerován pomocí Entity Framework Power Tools; vytvoření diagramu není součástí kurzu, jenom se tady používá jako obrázek.)

Student-Course_many-to-many_relationship

Každá čára relace má na jednom konci 1 a hvězdičku (*) na druhé, která označuje relaci 1:N.

Enrollment Pokud tabulka neobsahuje informace o známce, bude muset obsahovat pouze dva cizí klíče CourseID a StudentID. V takovém případě by to odpovídalo tabulce spojení M:N bez datové části (nebo tabulky čistého spojení) v databázi a nemuseli byste pro ni vůbec vytvářet třídu modelu. Course Entity Instructor mají takový typ relace M:N a jak vidíte, mezi nimi neexistuje žádná třída entit:

Many_relationship instruktora Course_many

V databázi se vyžaduje tabulka spojení, jak je znázorněno v následujícím diagramu databáze:

Many_relationship_tables instruktora Course_many

Entity Framework automaticky vytvoří CourseInstructor tabulku a vy ji čtete a aktualizujete nepřímo čtením a aktualizací Instructor.Courses vlastností a Course.Instructors navigace.

Diagram vztahu mezi entitami

Následující obrázek znázorňuje diagram, který nástroje Entity Framework Power Tools vytvářejí pro dokončený školní model.

School_data_model_diagram

Kromě čar relací M:N (* k *) a čar relací 1:N (1 až *) se zde můžete podívat na čáru relace 1:0 nebo 1 (1 až 0..1) mezi Instructor OfficeAssignment entitami relace 1:N a 1:N (0...1 až *) mezi entitami instruktora a oddělení.

Přidání kódu do kontextu databáze

Dále do třídy přidáte nové entity SchoolContext a přizpůsobíte některé mapování pomocí volání rozhraní API fluent. Rozhraní API je "fluent", protože je často používáno řetězcem řady volání metod do jednoho příkazu, jak je znázorněno v následujícím příkladu:

modelBuilder.Entity<Course>()
     .HasMany(c => c.Instructors).WithMany(i => i.Courses)
     .Map(t => t.MapLeftKey("CourseID")
         .MapRightKey("InstructorID")
         .ToTable("CourseInstructor"));

V tomto kurzu použijete rozhraní FLUENT API pouze pro mapování databáze, které nemůžete provádět s atributy. K určení většiny formátování, ověřování a mapování pravidel, která můžete provádět pomocí atributů, ale můžete také použít rozhraní API fluent. Některé atributy, jako MinimumLength je například nelze použít s rozhraním FLUENT API. Jak už jsme zmínili dříve, MinimumLength nezmění schéma, použije pouze ověřovací pravidlo na straně klienta a serveru.

Někteří vývojáři raději používají výhradně rozhraní API fluent, aby mohli třídy entit udržovat "čisté". Pokud chcete, můžete kombinovat atributy a rozhraní API fluent a existuje několik přizpůsobení, která se dají provést pouze pomocí rozhraní FLUENT API, ale obecně se doporučuje zvolit jeden z těchto dvou přístupů a použít ho co nejvíce konzistentně.

Pokud chcete do datového modelu přidat nové entity a provést mapování databáze, které jste neudělali pomocí atributů, nahraďte kód v DAL\SchoolContext.cs následujícím kódem:

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

Nový příkaz v metodě OnModelCreating konfiguruje tabulku spojení M:N:

  • Pro relaci M:N mezi Instructor entitami a Course entitami kód určuje názvy tabulek a sloupců pro tabulku spojení. Code First může nakonfigurovat relaci M:N pro vás bez tohoto kódu, ale pokud ji nezavoláte, zobrazí se výchozí názvy, jako InstructorInstructorID je sloupec InstructorID .

    modelBuilder.Entity<Course>()
        .HasMany(c => c.Instructors).WithMany(i => i.Courses)
        .Map(t => t.MapLeftKey("CourseID")
            .MapRightKey("InstructorID")
            .ToTable("CourseInstructor"));
    

Následující kód poskytuje příklad, jak byste mohli místo atributů použít rozhraní FLUENT API k určení vztahu mezi entitami Instructor OfficeAssignment :

modelBuilder.Entity<Instructor>()
    .HasOptional(p => p.OfficeAssignment).WithRequired(p => p.Instructor);

Informace o tom, co příkazy "fluent API" dělají na pozadí, najdete v blogovém příspěvku rozhraní Fluent API .

Počáteční databáze s testovacími daty

Nahraďte kód v souboru Migrations\Configuration.cs následujícím kódem, abyste mohli poskytnout počáteční data pro nové entity, které jste vytvořili.

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 jste viděli v prvním kurzu, většina tohoto kódu jednoduše aktualizuje nebo vytvoří nové objekty entity a načte ukázková data do vlastností podle potřeby pro testování. Všimněte si ale, jak se entita Course , která má vztah M:N s entitou Instructor , zpracovává:

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();

Při vytváření objektu Course inicializujete Instructors navigační vlastnost jako prázdnou kolekci pomocí kódu Instructors = new List<Instructor>(). Díky tomu můžete pomocí metody přidat Instructor entity, které s tím Course Instructors.Add souvisejí. Pokud jste nevytvořili prázdný seznam, nebudete moct tyto relace přidat, protože Instructors vlastnost by měla hodnotu null a neměla by metodu Add . Můžete také přidat inicializaci seznamu do konstruktoru.

Přidání migrace

V PMC zadejte add-migration příkaz (zatím tento příkaz neudělávejte update-database ):

add-Migration ComplexDataModel

Pokud jste se v tomto okamžiku update-database pokusili spustit příkaz (zatím ho neudělávejte), zobrazí se následující chyba:

Příkaz ALTER TABLE byl v konfliktu s omezením CIZÍ KLÍČ "FK_dbo. Course_dbo. Department_DepartmentID". Ke konfliktu došlo v databázi ContosoUniversity, tabulce dbo. Department", column 'DepartmentID'.

Někdy při provádění migrací s existujícími daty musíte do databáze vložit data zástupných procedur, aby vyhovovala omezením cizího klíče, a to je to, co teď musíte udělat. Vygenerovaný kód v ComplexDataModel Up metoda přidá do Course tabulky non-nullable DepartmentID cizí klíč. Vzhledem k tomu, že při spuštění kódu už jsou v Course tabulce řádky, operace selže, AddColumn protože SQL Server neví, jakou hodnotu má vložit do sloupce, který nemůže mít hodnotu null. Proto musíte změnit kód tak, aby nový sloupec měl výchozí hodnotu, a vytvořit oddělení zástupných procedur s názvem "Temp", které bude fungovat jako výchozí oddělení. V důsledku toho budou existující Course řádky po spuštění metody souviset s oddělením Up Temp. Můžete je spojit se správnými odděleními v Seed metodě.

<Upravte časové razítko>_ComplexDataModel.cs soubor, zakomentujte řádek kódu, který přidá sloupec DepartmentID do tabulky Course, a přidejte následující zvýrazněný kód (zvýrazněný je také řádek s komentáři):

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

Když se Seed metoda spustí, vloží řádky do Department tabulky a bude s těmito novými Department řádky souvisetCourse. Pokud jste do uživatelského rozhraní nepřidali žádné kurzy, už nebudete potřebovat oddělení Temp nebo výchozí hodnotu ve sloupci Course.DepartmentID . Pokud chcete umožnit, aby někdo přidal kurzy pomocí aplikace, měli byste také aktualizovat Seed kód metody, aby se zajistilo, že všechny Course řádky (nejen řádky vložené dříve spuštěním Seed metody) mají platné DepartmentID hodnoty, než odeberete výchozí hodnotu ze sloupce a odstraníte oddělení Temp.

Aktualizace databáze

Po dokončení úprav časového razítka<>_ComplexDataModel.cs souboru zadejte update-database příkaz v PMC a spusťte migraci.

update-database

Poznámka:

Při migraci dat a provádění změn schématu je možné získat další chyby. Pokud dojde k chybám migrace, které nemůžete vyřešit, můžete změnit název databáze v připojovací řetězec nebo databázi odstranit. Nejjednodušším přístupem je přejmenování databáze v souboru Web.config . Následující příklad ukazuje název, který se změnil na CU_Test:

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

U nové databáze se nemigrují žádná data a update-database příkaz se s mnohem větší pravděpodobností dokončí bez chyb. Pokyny k odstranění databáze najdete v tématu Jak odstranit databázi ze sady Visual Studio 2012.

Pokud to selže, můžete zkusit znovu inicializovat databázi zadáním následujícího příkazu do PMC:

update-database -TargetMigration:0

Otevřete databázi v Průzkumníku serveru, jak jste to udělali dříve, a rozbalte uzel Tabulky , abyste viděli, že všechny tabulky byly vytvořeny. (Pokud stále máte Průzkumník serveru se otevře dříve, klikněte na tlačítko Aktualizovat .)

Snímek obrazovky s oknem Průzkumníka serveru Složka Tabulky pod kontextem školy je otevřená.

Nevytvořili jste pro CourseInstructor tabulku třídu modelu. Jak bylo vysvětleno dříve, jedná se o tabulku spojení pro relaci M:N mezi entitami Instructor a Course entitami.

Klikněte pravým tlačítkem myši na CourseInstructor tabulku a vyberte Zobrazit data tabulky a ověřte, že obsahuje data v tabulce v důsledku Instructor entit, které jste přidali do Course.Instructors navigační vlastnosti.

Table_data_in_CourseInstructor_table

Získání kódu

Stáhnout dokončený projekt

Další materiály

Odkazy na další prostředky Entity Framework najdete v ASP.NET přístupu k datům – doporučené zdroje informací.

Další kroky

V tomto kurzu se naučíte:

  • Přizpůsobení datového modelu
  • Aktualizovaná entita Student
  • Vytvořená entita Instruktor
  • Vytvoření entity OfficeAssignment
  • Změna entity Course
  • Vytvoření entity Oddělení
  • Změna entity registrace
  • Přidání kódu do kontextu databáze
  • Počáteční databáze s testovacími daty
  • Přidání migrace
  • Aktualizace databáze

V dalším článku se dozvíte, jak číst a zobrazovat související data, která Entity Framework načítá do navigačních vlastností.