Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
EF Core använder en uppsättning konventioner när du identifierar och skapar en modell baserat på entitetstypklasser. Det här dokumentet sammanfattar de konventioner som används för att identifiera och konfigurera relationer mellan entitetstyper.
Viktigt!
De konventioner som beskrivs här kan åsidosättas genom explicit konfiguration av relationen med hjälp av antingen mappningsattribut eller modellskapande-API:et.
Tips/Råd
Koden nedan finns i RelationshipConventions.cs.
Upptäcka navigering
Relationsidentifieringen börjar med att identifiera navigeringer mellan entitetstyper .
Referensnavigeringar
En egenskap av en entitetstyp identifieras som en referensnavigering när:
- Fastigheten är offentlig.
- Boendet har en getter och en setter.
- Setter behöver inte vara offentlig; det kan vara privat eller ha någon annan åtkomstnivå.
- Settern kan endast vara Init.
- Egenskapstypen är, eller kan vara, en entitetstyp. Det innebär att typen
- Måste vara en referenstyp.
- Får inte uttryckligen ha konfigurerats som en primitiv egenskapstyp.
- Får inte mappas som en primitiv egenskapstyp av den databasprovider som används.
- Får inte automatiskt konverteras till en primitiv egenskapstyp som mappas av databasprovidern som används.
- Egenskapen är inte statisk.
- Egenskapen är inte en indexerareegenskap.
Tänk till exempel på följande entitetstyper:
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!;
}
För dessa typer upptäcks Blog.Author
och Author.Blog
som referensnavigeringar. Å andra sidan identifieras inte följande egenskaper som referensnavigeringar:
-
Blog.Id
, eftersomint
är en mappad primitiv typ -
Blog.Title
, eftersom "sträng" är en mappad primitiv typ -
Blog.Uri
, eftersomUri
konverteras automatiskt till en mappad primitiv typ -
Blog.ConsoleKeyInfo
, eftersomConsoleKeyInfo
är en C#-värdetyp -
Blog.DefaultAuthor
, eftersom egenskapen inte har någon setter -
Author.Id
, eftersomGuid
är en mappad primitiv typ -
Author.Name
, eftersom "sträng" är en mappad primitiv typ -
Author.BlogId
, eftersomint
är en mappad primitiv typ
Navigering i samlingar
En egenskap av en entitetstyp identifieras som en samlingsnavigering när:
- Fastigheten är offentlig.
- Boendet har en getter. Samlingsnavigeringar kan ha setters, men detta krävs inte.
- Egenskapstypen är eller implementerar
IEnumerable<TEntity>
, därTEntity
är eller kan vara en entitetstyp. Det innebär att typen avTEntity
:- Måste vara en referenstyp.
- Får inte uttryckligen ha konfigurerats som en primitiv egenskapstyp.
- Får inte mappas som en primitiv egenskapstyp av den databasprovider som används.
- Får inte automatiskt konverteras till en primitiv egenskapstyp som mappas av databasprovidern som används.
- Egenskapen är inte statisk.
- Egenskapen är inte en indexerareegenskap.
I följande kod identifieras till exempel både Blog.Tags
och Tag.Blogs
som samlingsnavigeringar:
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>();
}
Parkopplingsnavigeringar
När en navigering som går från till exempel entitetstyp A till entitetstyp B identifieras, måste den här gången avgöras om navigeringen har en invers som går i motsatt riktning, det vill säga från entitetstyp B till entitetstyp A. Om en sådan invers hittas kopplas de två navigeringarna ihop för att bilda en enda dubbelriktad relation.
Typen av relation bestäms av om navigeringen och dess invertering är referens- eller samlingsnavigeringar. Specifikt:
- Om en navigering är en samlingsnavigering och den andra är en referensnavigering är relationen en-till-många.
- Om båda navigeringerna är referensnavigeringar är relationen en-till-en.
- Om båda navigeringerna är samlingsnavigeringar är relationen många-till-många.
Identifiering av var och en av dessa typer av relationer visas i exemplen nedan:
En enda en-till-många-relation identifieras mellan Blog
och Post
identifieras genom parkoppling av navigeringsfälten Blog.Posts
och Post.Blog
:
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; }
}
En enda en-till-en-relation identifieras mellan Blog
och Author
identifieras genom parkoppling av navigeringsfälten Blog.Author
och 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; }
}
En enda, många-till-många-relation identifieras mellan Post
och Tag
identifieras genom att länka navigeringsfälten Post.Tags
och 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>();
}
Anmärkning
Den här parkopplingen av navigeringar kan vara felaktig om de två navigeringarna representerar två, olika, enkelriktade relationer. I det här fallet måste de två relationerna konfigureras explicit.
Parkoppling av relationer fungerar bara när det finns en enda relation mellan två typer. Flera relationer mellan två typer måste konfigureras explicit.
Anmärkning
Beskrivningarna här gäller relationer mellan två olika typer. Det är dock möjligt för samma typ att vara i båda ändar av en relation, och därför för en enda typ att ha två navigeringer som båda är kopplade till varandra. Detta kallas en självrefererande relation.
Upptäcka egenskaper hos främmande nyckel
När navigeringarna för en relation antingen har identifierats eller konfigurerats explicit används dessa för att upptäcka lämpliga egenskaper för foreign key för relationen. En egenskap identifieras som en främmande nyckel när:
- Egenskapstypen är kompatibel med den primära eller alternativa nyckeln för huvudentitetstypen.
- Typer är kompatibla om de är desamma, eller om egenskapstypen för främmande nyckel är en nullvärdig version av den primära eller alternativa nyckelegenskapstypen.
- Egenskapsnamnet matchar en av namngivningskonventionerna för en främmande nyckelegenskap. Namngivningskonventionerna är:
<navigation property name><principal key property name>
<navigation property name>Id
<principal entity type name><principal key property name>
<principal entity type name>Id
- Om den beroende änden dessutom uttryckligen har konfigurerats med hjälp av modellskapande-API:et och den beroende primärnyckeln är kompatibel, används även den beroende primärnyckeln som sekundärnyckel.
Tips/Råd
Suffixet "ID" kan ha valfritt hölje.
Följande entitetstyper visar exempel för var och en av dessa namngivningskonventioner.
Post.TheBlogKey
identifieras som främmande nyckel eftersom den matchar mönstret <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
identifieras som främmande nyckel eftersom den matchar mönstret <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
identifieras som främmande nyckel eftersom den matchar mönstret <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
identifieras som främmande nyckel eftersom den matchar mönstret <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; }
}
Anmärkning
När det gäller en-till-många-navigeringar måste egenskaperna för främmande nyckeln finnas på typen med referensnavigeringen, eftersom detta blir den beroende entiteten. När det gäller en-till-en-relationer används identifiering av en främmande nyckelegenskap för att avgöra vilken typ som representerar den beroende delen av relationen. Om ingen sekundärnyckelegenskap identifieras måste den beroende änden konfigureras med HasForeignKey
. Exempel på detta finns i En-till-en-relationer .
Reglerna ovan gäller även för sammansatta sekundärnycklar, där varje egenskap för den sammansatta måste ha en kompatibel typ med motsvarande egenskap för den primära eller alternativa nyckeln, och varje egenskapsnamn måste matcha någon av de namngivningskonventioner som beskrivs ovan.
Fastställa kardinalitet
EF använder de identifierade navigeringarna och foreign key-egenskaperna för att fastställa kardinaliteten för relationen tillsammans med dess huvudsakliga och beroende ändar.
- Om det finns en oparad referensnavigering konfigureras relationen som enkelriktad en-till-många, med referensnavigering på den beroende änden.
- Om det finns en oparad navigering för samlingen konfigureras relationen som en enkelriktad en-till-många med navigering för samlingen på huvudänden.
- Om det finns länkade referens- och samlingsnavigeringar konfigureras relationen som en dubbelriktad en-till-många med samlingsnavigering i huvudänden.
- Om en referensnavigering är kopplad till en annan referensnavigering:
- Om en främmande nyckelegenskap identifierades på ena sidan men inte den andra, konfigureras relationen som en tvåvägs en-till-en, med främmande nyckelegenskapen på den beroende sidan.
- I annat fall går det inte att fastställa den beroende sidan och EF utlöser ett undantag som anger att den beroende måste konfigureras uttryckligen.
- Om en samlingsnavigering är kopplad till en annan samlingsnavigering konfigureras relationen som en dubbelriktad många-till-många.
Egenskaper för extern nyckel för skuggad
Om EF har fastställt det beroende slutet av relationen men ingen sekundärnyckelegenskap har identifierats skapar EF en skuggegenskap som representerar den externa nyckeln. Skuggegenskapen:
- Har typen av den primära eller alternativa nyckelegenskapen vid den huvudsakliga änden av relationen.
- Typen är null som standard, vilket gör relationen valfri som standard.
- Om det finns en navigering på den beroende änden namnges egenskapen för skuggsekundärnyckeln med navigeringsnamnet sammanfogat med det primära eller alternativa nyckelegenskapsnamnet.
- Om det inte finns någon navigering på den beroende sidan, namnges skuggan av främmande nyckelegenskap med entitetstypens namn, sammanfogat med det primära eller alternativa nyckelegenskapsnamnet.
Kaskadborttagning
Enligt konventionen konfigureras nödvändiga relationer för kaskadborttagning. Valfria relationer är konfigurerade för att inte utföra kaskad deletion.
Många-till-många
Många-till-många-relationer har inte huvud- och beroendeändar, och ingen av ändarna innehåller en egenskap för främmande nyckel. I stället använder många-till-många-relationer en kopplingsentitetstyp som innehåller par med sekundärnycklar som pekar på någon av slutpunkterna för många-till-många. Överväg följande entitetstyper, för vilka en många-till-många-relation identifieras av konventionen:
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>();
}
De konventioner som används i den här identifieringen är:
- Anslutningsentitetstypen heter
<left entity type name><right entity type name>
. Så iPostTag
det här exemplet.- Kopplingstabellen har samma namn som anslutningsentitetstypen.
- Kopplingsentiteten ges en utländsk nyckelegenskap för varje riktning i relationen. Dessa heter
<navigation name><principal key name>
. I det här exemplet är egenskaperna för främmande nyckelPostsId
ochTagsId
.- För en enkelriktad många-till-många relation är den främmande nyckeln utan associerad navigering benämnd
<principal entity type name><principal key name>
.
- För en enkelriktad många-till-många relation är den främmande nyckeln utan associerad navigering benämnd
- Egenskaperna för främmande nyckel är icke-nullbara, vilket gör både relationerna till kopplingsentiteten nödvändiga.
- Kaskadborttagningskonventionerna innebär att dessa relationer konfigureras för kaskadborttagning.
- Anslutningsentitetstypen konfigureras med en sammansatt primärnyckel som består av de två foreign key-egenskaperna. I det här exemplet består alltså primärnyckeln av
PostsId
ochTagsId
.
Detta resulterar i följande EF-modell:
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
Och översätts till följande databasschema när du använder SQLite:
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");
Indexen
Enligt konventionen skapar EF ett databasindex för egenskapen eller egenskaperna för en sekundärnyckel. Vilken typ av index som skapas bestäms av:
- Kardinaliteten för relationen
- Om relationen är valfri eller obligatorisk
- Antalet egenskaper som utgör den främmande nyckeln
För en en-till-många-relation skapas ett enkelt index av konventionen. Samma index skapas för valfria och obligatoriska relationer. Till exempel på SQLite:
CREATE INDEX "IX_Post_BlogId" ON "Post" ("BlogId");
Eller på SQL Server:
CREATE INDEX [IX_Post_BlogId] ON [Post] ([BlogId]);
För en obligatorisk en-till-en-relation skapas ett unikt index. Till exempel på SQLite:
CREATE UNIQUE INDEX "IX_Author_BlogId" ON "Author" ("BlogId");
Eller på SQL Sever:
CREATE UNIQUE INDEX [IX_Author_BlogId] ON [Author] ([BlogId]);
För valfria en-till-en-relationer är indexet som skapats på SQLite detsamma:
CREATE UNIQUE INDEX "IX_Author_BlogId" ON "Author" ("BlogId");
Men på SQL Server läggs ett IS NOT NULL
-filter till för att bättre hantera nullvärden för främmande nycklar. Till exempel:
CREATE UNIQUE INDEX [IX_Author_BlogId] ON [Author] ([BlogId]) WHERE [BlogId] IS NOT NULL;
För sammansatta sekundärnycklar skapas ett index som täcker alla sekundärnyckelkolumner. Till exempel:
CREATE INDEX "IX_Post_ContainingBlogId1_ContainingBlogId2" ON "Post" ("ContainingBlogId1", "ContainingBlogId2");
Anmärkning
EF skapar inte index för egenskaper som redan omfattas av ett befintligt index eller primärnyckelvillkor.
Så här hindrar du EF från att skapa index för sekundärnycklar
Index har omkostnader, och som du frågar här kanske det inte alltid är lämpligt att skapa dem för alla FK-kolumner. För att uppnå detta ForeignKeyIndexConvention
kan du ta bort när du skapar modellen:
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.Conventions.Remove(typeof(ForeignKeyIndexConvention));
}
Vid behov kan index fortfarande uttryckligen skapas för de främmande nyckelkolumner som behöver dem.
Namn på begränsningar för extern nyckel
Enligt konventionen heter begränsningar för utländska nycklar FK_<dependent type name>_<principal type name>_<foreign key property name>
. För sammansatta främmande nycklar, <foreign key property name>
blir en understrecksavgränsad lista över egenskapsnamn för främmande nyckel.