Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
EF Core používá sadu konvencí při zjišťování a vytváření modelu na základě tříd typů entit. Tento dokument shrnuje konvence používané ke zjišťování a konfiguraci vztahů mezi typy entit.
Důležité
Zde popsané konvence je možné přepsat explicitní konfigurací relace pomocí atributů mapování nebo rozhraní API pro sestavení modelu.
Návod
Níže uvedený kód najdete v RelationshipConventions.cs.
Objevování navigací
Zjišťování relací začíná zjišťováním navigace mezi typy entit.
Referenční navigace
Vlastnost typu entity je zjištěna jako referenční navigace v těchto případech:
- Vlastnost je veřejná.
- Vlastnost má getter a setter.
- Setter nemusí být veřejný; může být soukromý nebo může mít jakoukoli jinou přístupnost.
- Setter může být jen inicializační.
- Typ vlastnosti je nebo může být typem entity. To znamená, že typ
- Musí to být odkazový typ.
- Nesmí být nakonfigurován explicitně jako primitivní typ vlastnosti.
- Nesmí být mapován jako primitivní typ vlastnosti používaného poskytovatelem databáze.
- Nesmí se automaticky převést na primitivní typ vlastnosti namapovaný poskytovatelem databáze, který používá.
- Vlastnost není statická.
- Vlastnost není vlastnost indexátoru.
Představte si například následující typy entit:
public class Blog
{
// Not discovered as reference navigations:
public int Id { get; set; }
public string Title { get; set; } = null!;
public Uri? Uri { get; set; }
public ConsoleKeyInfo ConsoleKeyInfo { get; set; }
public Author DefaultAuthor => new() { Name = $"Author of the blog {Title}" };
// Discovered as a reference navigation:
public Author? Author { get; private set; }
}
public class Author
{
// Not discovered as reference navigations:
public Guid Id { get; set; }
public string Name { get; set; } = null!;
public int BlogId { get; set; }
// Discovered as a reference navigation:
public Blog Blog { get; init; } = null!;
}
Blog.Author a Author.Blog jsou zjištěny jako referenční navigace pro tyto typy. Na druhou stranu se jako referenční navigace nezjistí následující vlastnosti:
-
Blog.Id, protožeintje mapovaný primitivní typ -
Blog.Title, protože 'string' je mapovaný primitivní typ -
Blog.Uri, protožeUrije automaticky převeden na mapovaný primitivní typ -
Blog.ConsoleKeyInfo, protožeConsoleKeyInfoje typ hodnoty jazyka C#. -
Blog.DefaultAuthor, protože vlastnost nemá setter -
Author.Id, protožeGuidje mapovaný primitivní typ -
Author.Name, protože 'string' je mapovaný primitivní typ -
Author.BlogId, protožeintje mapovaný primitivní typ
Navigace v kolekcích
Vlastnost typu entity se zjistí jako navigace v kolekci v těchto případech:
- Vlastnost je veřejná.
- Vlastnost má getter. Navigace v kolekcích mohou mít nastavovače, ale nejsou povinné.
- Typ vlastnosti je nebo implementuje
IEnumerable<TEntity>, kdeTEntityje nebo může být typ entity. To znamená, že typTEntity:- Musí to být odkazový typ.
- Nesmí být nakonfigurován explicitně jako primitivní typ vlastnosti.
- Nesmí být mapován jako primitivní typ vlastnosti používaného poskytovatelem databáze.
- Nesmí se automaticky převést na primitivní typ vlastnosti namapovaný poskytovatelem databáze, který používá.
- Vlastnost není statická.
- Vlastnost není vlastnost indexátoru.
Například v následujícím kódu jsou obě Blog.Tags a Tag.Blogs zjištěny jako navigace sbírky.
public class Blog
{
public int Id { get; set; }
public List<Tag> Tags { get; set; } = null!;
}
public class Tag
{
public Guid Id { get; set; }
public IEnumerable<Blog> Blogs { get; } = new List<Blog>();
}
Párování navigací
Jakmile je objevena navigace jdoucí například od typu entity A k typu entity B, je třeba zjistit, zda má tato navigace inverzní navigaci opačným směrem – to znamená od typu entity B k typu entity A. Pokud je taková inverzní navigace nalezena, obě navigace jsou spárovány dohromady a tvoří jednu obousměrnou relaci.
Typ vztahu je určen podle toho, zda navigace a její opačné navigace jsou typu reference nebo kolekce. Konkrétně:
- Pokud je jedna navigace kolekční navigací a druhá je referenční navigace, je relace jeden k mnoha.
- Pokud jsou obě navigace referenčními navigacemi, je relace 1:1.
- Pokud jsou obě navigace navigacemi kolekcí, je relace mnoho na mnoho.
Zjišťování každého z těchto typů relací je znázorněno v následujících příkladech:
Jednoduchý vztah 1:N mezi Blog a Post je zjištěn pomocí párování Blog.Posts a Post.Blog navigací.
public class Blog
{
public int Id { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public int Id { get; set; }
public int? BlogId { get; set; }
public Blog? Blog { get; set; }
}
Jediný vztah 1:1 mezi Blog a Author je zjištěn spárováním navigací Blog.Author a Author.Blog.
public class Blog
{
public int Id { get; set; }
public Author? Author { get; set; }
}
public class Author
{
public int Id { get; set; }
public int? BlogId { get; set; }
public Blog? Blog { get; set; }
}
Je zjištěna jedna relace M:N mezi Post a Tag spárováním navigací Post.Tags a Tag.Posts.
public class Post
{
public int Id { get; set; }
public ICollection<Tag> Tags { get; } = new List<Tag>();
}
public class Tag
{
public int Id { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
Poznámka:
Toto párování navigace může být nesprávné, pokud dvě navigace představují dvě, různé, jednosměrné relace. V tomto případě musí být tyto dvě relace nakonfigurované explicitně.
Párování relací funguje jenom v případech, kdy existuje jeden vztah mezi dvěma typy. Explicitně musí být nakonfigurované více relací mezi dvěma typy.
Poznámka:
Popisy jsou z hlediska vztahů mezi dvěma různými typy. Je však možné, aby byl stejný typ na obou koncích vztahu, a proto aby měl jeden typ dvě navigace spárované navzájem. Tomu se říká vztah odkazující sám na sebe.
Zjišťování vlastností cizího klíče
Po zjištění nebo explicitní konfiguraci navigace pro relaci se tyto navigace použijí ke zjištění odpovídajících vlastností cizího klíče pro relaci. Vlastnost se identifikuje jako cizí klíč, když:
- Typ vlastnosti je kompatibilní s primárním nebo alternativním klíčem u typu hlavní entity.
- Typy jsou kompatibilní, pokud jsou stejné, nebo pokud je typ vlastnosti cizího klíče typem vlastnosti primárního nebo alternativního klíče, který může mít hodnotu null.
- Název vlastnosti odpovídá jedné z konvencí pojmenování pro vlastnosti cizího klíče. Zásady vytváření názvů jsou:
<navigation property name><principal key property name><navigation property name>Id<principal entity type name><principal key property name><principal entity type name>Id
- Kromě toho, pokud byl konec závislosti explicitně nakonfigurován pomocí rozhraní API pro vytváření modelů a závislý primární klíč je kompatibilní, použije se závislý primární klíč také jako cizí klíč.
Návod
Přípona "ID" může mít libovolnou velikost písmen.
Následující typy entit zobrazují příklady pro každou z těchto konvencí pojmenování.
Post.TheBlogKey je zjištěn jako cizí klíč, protože odpovídá vzoru <navigation property name><principal key property name>:
public class Blog
{
public int Key { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public int Id { get; set; }
public int? TheBlogKey { get; set; }
public Blog? TheBlog { get; set; }
}
Post.TheBlogID je zjištěn jako cizí klíč, protože odpovídá vzoru <navigation property name>Id:
public class Blog
{
public int Key { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public int Id { get; set; }
public int? TheBlogID { get; set; }
public Blog? TheBlog { get; set; }
}
Post.BlogKey je zjištěn jako cizí klíč, protože odpovídá vzoru <principal entity type name><principal key property name>:
public class Blog
{
public int Key { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public int Id { get; set; }
public int? BlogKey { get; set; }
public Blog? TheBlog { get; set; }
}
Post.Blogid je zjištěn jako cizí klíč, protože odpovídá vzoru <principal entity type name>Id:
public class Blog
{
public int Key { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public int Id { get; set; }
public int? Blogid { get; set; }
public Blog? TheBlog { get; set; }
}
Poznámka:
V případě navigace typu 1:N musí být vlastnosti cizího klíče u typu s odkazovou navigací, protože tento typ bude závislou entitou. V případě relací 1:1 se zjišťování vlastnosti cizího klíče používá k určení typu, který představuje závislý konec relace. Pokud není zjištěna žádná vlastnost cizího klíče, musí být závislý konec nakonfigurován pomocí HasForeignKey. Příklady těchto relací najdete v tématu Relace 1:1 .
Výše uvedená pravidla platí také pro složené cizí klíče, kde každá vlastnost složeného souboru musí mít kompatibilní typ s odpovídající vlastností primárního nebo alternativního klíče a každý název vlastnosti musí odpovídat jedné z výše popsaných zásad vytváření názvů.
Určení kardinality
Ef používá zjištěné navigace a vlastnosti cizího klíče k určení kardinality relace společně s jeho hlavními a závislými konci:
- Pokud existuje jedna, nespárovaná referenční navigace, je relace nakonfigurována jako jednosměrná jedna k mnoha s referenční navigací na závislém konci.
- Pokud existuje jedna nepárová navigace kolekce, relace se nakonfiguruje jako jednosměrná jedna ku mnoha, s navigací kolekce na konci hlavního objektu.
- Pokud existují spárované odkazy a navigace kolekcí, je relace nakonfigurována jako obousměrná jedna k mnoha 1:N, přičemž navigace kolekcí je na hlavním konci.
- Pokud je navigace s odkazem spárovaná s jinou odkazovou navigaci, postupujte takto:
- Pokud byla vlastnost cizího klíče zjištěna na jedné straně, ale ne na druhé, je relace nakonfigurována jako obousměrný 1:1 s vlastností cizího klíče na závislém konci.
- V opačném případě nelze určit závislou stranu a EF vyvolá výjimku, která indikuje, že závislá strana je nutné explicitně nakonfigurovat.
- Pokud je navigace v kolekci spárovaná s jinou navigaci v kolekci, je relace nakonfigurována jako obousměrný M :N.
Vlastnosti stínového cizího klíče
Pokud ef určil závislý konec relace, ale nebyla zjištěna žádná vlastnost cizího klíče, ef vytvoří stínovou vlastnost představující cizí klíč. Stínová vlastnost:
- Má typ vlastnosti primárního nebo alternativního klíče na hlavním konci relace.
- Typ je automaticky nastaven jako nulovatelný, což činí relaci volitelnou.
- Pokud je na závislém konci navigace, vlastnost stínového cizího klíče má název tvořený tímto názvem navigace spojeným s názvem vlastnosti primárního nebo alternativního klíčového prvku.
- Pokud na závislé straně chybí navigace, je vlastnost stínového cizího klíče pojmenována spojením názvu typu hlavní entity s názvem vlastnosti primárního nebo alternativního klíče.
Kaskádové odstranění
Podle konvence jsou požadované relace nakonfigurované tak, aby se provedlo kaskádové mazání. Volitelné relace jsou nakonfigurovány tak, aby neprováděly kaskádové odstranění.
mnoho-na-mnoho
Relace M:N nemají hlavní a závislé konce a žádný z konců nezahrnuje vlastnost cizího klíče. Místo toho relace M:N používají typ spojovací entity, který obsahuje páry cizích klíčů odkazujících na oba konce této relace. Zvažte následující typy entit, pro které je konvencí zjištěný vztah mnoha ku mnoha.
public class Post
{
public int Id { get; set; }
public ICollection<Tag> Tags { get; } = new List<Tag>();
}
public class Tag
{
public int Id { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
Konvence používané v tomto zjišťování jsou:
- Typ entity join má název
<left entity type name><right entity type name>.PostTagTakže v tomto příkladu.- Tabulka spojení má stejný název jako typ entity spojení.
- Typ entity join má vlastnost cizího klíče pro každý směr relace. Ty se jmenují
<navigation name><principal key name>. V tomto příkladu jsou vlastnosti cizího klíčePostsIdaTagsId.- Pro jednosměrný vztah typu mnoho k mnoha, má vlastnost cizího klíče bez přidružené navigace název
<principal entity type name><principal key name>.
- Pro jednosměrný vztah typu mnoho k mnoha, má vlastnost cizího klíče bez přidružené navigace název
- Vlastnosti cizího klíče jsou nenulovatelné, což znamená, že oba vztahy k entitě spojení jsou povinné.
- Konvence kaskádového odstranění znamenají, že tyto relace budou nakonfigurovány pro kaskádové odstranění.
- Propojovací entita je nastavena se složeným primárním klíčem složeným ze dvou vlastností cizího klíče. V tomto příkladu je tedy primární klíč tvořen
PostsIdaTagsId.
Výsledkem je následující model EF:
Model:
EntityType: Post
Properties:
Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd
Skip navigations:
Tags (ICollection<Tag>) CollectionTag Inverse: Posts
Keys:
Id PK
EntityType: Tag
Properties:
Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd
Skip navigations:
Posts (ICollection<Post>) CollectionPost Inverse: Tags
Keys:
Id PK
EntityType: PostTag (Dictionary<string, object>) CLR Type: Dictionary<string, object>
Properties:
PostsId (no field, int) Indexer Required PK FK AfterSave:Throw
TagsId (no field, int) Indexer Required PK FK Index AfterSave:Throw
Keys:
PostsId, TagsId PK
Foreign keys:
PostTag (Dictionary<string, object>) {'PostsId'} -> Post {'Id'} Cascade
PostTag (Dictionary<string, object>) {'TagsId'} -> Tag {'Id'} Cascade
Indexes:
TagsId
A při použití SQLite se přeloží na následující schéma databáze:
CREATE TABLE "Posts" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_Posts" PRIMARY KEY AUTOINCREMENT);
CREATE TABLE "Tag" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_Tag" PRIMARY KEY AUTOINCREMENT);
CREATE TABLE "PostTag" (
"PostsId" INTEGER NOT NULL,
"TagsId" INTEGER NOT NULL,
CONSTRAINT "PK_PostTag" PRIMARY KEY ("PostsId", "TagsId"),
CONSTRAINT "FK_PostTag_Posts_PostsId" FOREIGN KEY ("PostsId") REFERENCES "Posts" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_PostTag_Tag_TagsId" FOREIGN KEY ("TagsId") REFERENCES "Tag" ("Id") ON DELETE CASCADE);
CREATE INDEX "IX_PostTag_TagsId" ON "PostTag" ("TagsId");
Rejstříky
Ef vytvoří index databáze pro vlastnost nebo vlastnosti cizího klíče. Typ vytvořeného indexu určuje:
- Kardinalita relace
- Bez ohledu na to, jestli je relace volitelná nebo povinná
- Počet vlastností, které tvoří cizí klíč
Pro relaci 1:N se podle konvence vytvoří jednoduchý index. Stejný index se vytvoří pro volitelné a požadované relace. Například na SQLite:
CREATE INDEX "IX_Post_BlogId" ON "Post" ("BlogId");
Nebo na SQL Serveru:
CREATE INDEX [IX_Post_BlogId] ON [Post] ([BlogId]);
Pro požadovanou relaci 1:1 se vytvoří jedinečný index. Například na SQLite:
CREATE UNIQUE INDEX "IX_Author_BlogId" ON "Author" ("BlogId");
Nebo na SQL Serveru:
CREATE UNIQUE INDEX [IX_Author_BlogId] ON [Author] ([BlogId]);
U volitelných relací 1:1 je index vytvořený na SQLite stejný:
CREATE UNIQUE INDEX "IX_Author_BlogId" ON "Author" ("BlogId");
Na SQL Serveru se ale přidá filtr pro IS NOT NULL lepší zpracování nulových hodnot v cizích klíčích. Například:
CREATE UNIQUE INDEX [IX_Author_BlogId] ON [Author] ([BlogId]) WHERE [BlogId] IS NOT NULL;
Pro složené cizí klíče se vytvoří index pokrývající všechny sloupce cizího klíče. Například:
CREATE INDEX "IX_Post_ContainingBlogId1_ContainingBlogId2" ON "Post" ("ContainingBlogId1", "ContainingBlogId2");
Poznámka:
EF nevytvoří indexy pro vlastnosti, které jsou již pokryty existujícím omezením indexu nebo primárního klíče.
Jak zastavit vytváření indexů EF pro cizí klíče
Indexy mají režijní náklady a jak je zde uvedeno, nemusí být vždy vhodné je vytvořit pro všechny sloupce FK. Abyste toho dosáhli, ForeignKeyIndexConvention můžete ho při sestavování modelu odebrat:
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.Conventions.Remove(typeof(ForeignKeyIndexConvention));
}
V případě potřeby je možné indexy i nadále explicitně vytvářet pro sloupce cizího klíče, které je potřebují.
Názvy omezení cizího klíče
Omezení cizího klíče podle konvence jsou pojmenována FK_<dependent type name>_<principal type name>_<foreign key property name>. Pro složené cizí klíče se <foreign key property name> mění na seznam názvů vlastností cizího klíče oddělený podtržítky.