Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Program EF Core używa zestawu konwencji podczas odnajdywania i tworzenia modelu na podstawie klas typów jednostek. Ten dokument zawiera podsumowanie konwencji używanych do odnajdywania i konfigurowania relacji między typami jednostek.
Ważne
Konwencje opisane tutaj można zastąpić jawną konfiguracją relacji przy użyciu atrybutów mapowania lub interfejsu API tworzenia modelu.
Wskazówka
Poniższy kod można znaleźć w RelationshipConventions.cs.
Odnajdywanie nawigacji
Odnajdywanie relacji rozpoczyna się od odnajdywania nawigacji między typami jednostek.
Nawigacje referencyjne
Właściwość typu jednostki jest wykrywana jako nawigacja referencyjna w przypadku:
- Obiekt jest publiczny.
- Właściwość posiada getter oraz setter.
- Setter nie musi być publiczny; może być prywatny lub mieć inne ułatwienia dostępu.
- Elementem ustawianym może być tylko inicjowanie.
- Typ właściwości to lub może być typem jednostki. Oznacza to, że typ
- Musi być typem odwołania.
- Nie wolno było jawnie skonfigurować jako typ właściwości pierwotnej.
- Nie może być mapowany jako typ właściwości pierwotnej używany przez dostawcę bazy danych.
- Nie może być automatycznie konwertowany na typ właściwości pierwotnej mapowany przez używanego dostawcę bazy danych.
- Właściwość nie jest statyczna.
- Właściwość nie jest właściwością indeksatora.
Rozważmy na przykład następujące typy jednostek:
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!;
}
Dla tych typów Blog.Author i Author.Blog są zidentyfikowane jako nawigacje referencyjne. Z drugiej strony, następujące właściwości nie są uznawane jako nawigacje referencyjne:
-
Blog.Id, ponieważintjest mapowanym typem pierwotnym -
Blog.Title, ponieważ 'string' jest mapowanym typem prymitywnym -
Blog.Uri, ponieważUrijest automatycznie konwertowany na zamapowany typ pierwotny -
Blog.ConsoleKeyInfo, ponieważConsoleKeyInfojest typem wartości języka C# -
Blog.DefaultAuthor, ponieważ właściwość nie ma metody ustawiającej -
Author.Id, ponieważGuidjest mapowanym typem pierwotnym -
Author.Name, ponieważ 'string' jest mapowanym typem prymitywnym -
Author.BlogId, ponieważintjest mapowanym typem pierwotnym
Nawigacje po kolekcjach
Właściwość typu jednostki jest wykrywana jako nawigacja po kolekcji , gdy:
- Obiekt jest publiczny.
- Właściwość posiada getter. Nawigacje kolekcji mogą mieć metody ustawiające, ale nie jest to wymagane.
- Typ właściwości to
IEnumerable<TEntity>lub implementujeIEnumerable<TEntity>, gdzie jest lub może być typem jednostki. Oznacza to, że typTEntity:- Musi być typem odwołania.
- Nie wolno było jawnie skonfigurować jako typ właściwości pierwotnej.
- Nie może być mapowany jako typ właściwości pierwotnej używany przez dostawcę bazy danych.
- Nie może być automatycznie konwertowany na typ właściwości pierwotnej mapowany przez używanego dostawcę bazy danych.
- Właściwość nie jest statyczna.
- Właściwość nie jest właściwością indeksatora.
Na przykład w poniższym kodzie zarówno Blog.Tags, jak i Tag.Blogs są wykrywane jako nawigacje kolekcji.
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>();
}
Parowanie nawigacji
Gdy na przykład zostanie odkryta nawigacja przechodząca od typu jednostki A do typu jednostki B, należy określić, czy ma ona odwrotność w postaci nawigacji od typu jednostki B do typu jednostki A. Jeśli taka odwrotność zostanie znaleziona, obie nawigacje są łączone, aby utworzyć jedną, dwukierunkową relację.
Typ relacji zależy od tego, czy nawigacja i jej odwrotność to nawigacja referencyjna, czy nawigacja kolekcji. Specyficznie:
- Jeśli jedna nawigacja to nawigacja kolekcji, a druga to nawigacja referencyjna, relacja to jeden do wielu.
- Jeśli obie nawigacje są nawigacjami referencyjnymi, relacja to jeden do jednego.
- Jeśli obie nawigacje to nawigacje kolekcji, relacja to wiele do wielu.
Odnajdywanie każdego z tych typów relacji przedstawiono w poniższych przykładach:
Relacja typu jeden do wielu między elementami Blog i Post zostaje odkryta przez sparowanie Blog.Posts i Post.Blog nawigacji.
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; }
}
Jedna relacja "jeden do jednego" między elementami Blog i Author jest odkrywana przez parowanie nawigacji Blog.Author i 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; }
}
Pojedyncza relacja wiele-do-wielu jest odkrywana między Post a Tag poprzez sparowanie nawigacji Post.Tags i 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>();
}
Uwaga / Notatka
To parowanie nawigacji może być nieprawidłowe, jeśli obie nawigacje reprezentują dwie, różne, jednokierunkowe relacje. W takim przypadku należy jawnie skonfigurować dwie relacje.
Parowanie relacji działa tylko wtedy, gdy istnieje jedna relacja między dwoma typami. Należy jawnie skonfigurować wiele relacji między dwoma typami.
Uwaga / Notatka
Opisy w tym miejscu dotyczą relacji między dwoma różnymi typami. Istnieje jednak możliwość, że ten sam typ znajduje się na obu końcach relacji, a w związku z tym jeden typ posiada dwie nawigacje powiązane ze sobą. Relacja ta jest nazywana relacją odwołującą się do siebie.
Rozpoznawanie właściwości klucza obcego
Po odkryciu lub jawnym skonfigurowaniu nawigacji dla relacji, są one wykorzystywane zarówno do odkrywania, jak i do konfiguracji odpowiednich właściwości klucza obcego dla tej relacji. Właściwość jest rozpoznawana jako klucz obcy, gdy:
- Typ właściwości jest zgodny z kluczem podstawowym lub alternatywnym w typie jednostki głównej.
- Typy są zgodne, jeśli są takie same lub jeśli typ właściwości klucza obcego jest wersją typu właściwości klucza głównego lub alternatywnego, która dopuszcza wartość null.
- Nazwa właściwości odpowiada jednej konwencji nazewniczej dotyczącej właściwości klucza obcego. Konwencje nazewnictwa to:
<navigation property name><principal key property name><navigation property name>Id<principal entity type name><principal key property name><principal entity type name>Id
- Ponadto, jeśli koniec zależny został jawnie skonfigurowany za pomocą interfejsu API tworzenia modelu, a klucz podstawowy obiektu zależnego jest zgodny, to klucz ten będzie również używany jako klucz obcy.
Wskazówka
Sufiks "Id" może być zapisana w dowolnej wielkości liter.
W poniższych typach jednostek przedstawiono przykłady dla każdej z tych konwencji nazewnictwa.
Post.TheBlogKey jest wykrywany jako klucz obcy, ponieważ pasuje do wzorca <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 jest wykrywany jako klucz obcy, ponieważ pasuje do wzorca <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 jest wykrywany jako klucz obcy, ponieważ pasuje do wzorca <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 jest wykrywany jako klucz obcy, ponieważ pasuje do wzorca <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; }
}
Uwaga / Notatka
W przypadku nawigacji "jeden do wielu" właściwości klucza obcego muszą znajdować się w typie z nawigacją referencyjną, ponieważ będzie to jednostka zależna. W przypadku relacji jeden do jednego zidentyfikowanie właściwości klucza obcego służy do określenia, który typ wskazuje na zależny koniec relacji. Jeśli nie zostanie odnaleziona żadna właściwość klucza obcego, to koniec zależny musi być skonfigurowany przy użyciu HasForeignKey. Aby zapoznać się z przykładami, zobacz Relacje jeden do jednego .
Powyższe reguły dotyczą również złożonych kluczy obcych, gdzie każda właściwość złożonego musi mieć zgodny typ z odpowiednią właściwością klucza podstawowego lub alternatywnego, a każda nazwa właściwości musi być zgodna z jedną z konwencji nazewnictwa opisanych powyżej.
Określanie kardynalności
EF używa odnalezionych właściwości nawigacyjnych i kluczy obcych, aby określić kardynalność relacji wraz z jej elementami głównymi i zależnymi.
- Jeśli istnieje nieparzysta nawigacja referencyjna, relacja jest skonfigurowana jako jednokierunkowa jeden do wielu, z nawigacją referencyjną na końcu zależnym.
- Jeśli istnieje jedna, niepowiązana nawigacja kolekcji, relacja zostaje skonfigurowana jako jednokierunkowa jeden do wielu, z nawigacją kolekcji na końcu głównego obiektu.
- Jeśli istnieją sparowane odwołania i nawigacje kolekcji, relacja jest skonfigurowana jako dwukierunkowy jeden do wielu, z nawigacją kolekcji na końcu nadrzędnym.
- Jeśli nawigacja referencyjna jest sparowana z inną nawigacją referencyjną, wówczas:
- Jeśli właściwość klucza obcego została odnaleziona po jednej stronie, ale nie po drugiej, relacja jest skonfigurowana jako dwukierunkowa relacja jeden do jednego, z właściwością klucza obcego na końcu zależnym.
- W przeciwnym razie nie można określić strony zależnej i EF zgłasza wyjątek wskazujący, że zależność musi być jawnie skonfigurowana.
- Jeśli nawigacja kolekcji jest sparowana z inną nawigacją kolekcji, relacja jest skonfigurowana jako dwukierunkowa relacja wiele-do-wielu.
Właściwości ukrytego klucza obcego
Jeśli EF ustalił zależny koniec relacji, ale nie odnaleziono właściwości klucza obcego, wtedy program EF utworzy ukrytą właściwość reprezentującą klucz obcy. Właściwość cienia:
- Ma typ właściwości klucza podstawowego lub alternatywnego na końcu głównej relacji.
- Typ jest domyślnie dopuszczany do wartości null, co sprawia, że relacja jest domyślnie opcjonalna.
- Jeśli na końcu zależnym znajduje się nawigacja, właściwość ukrytego klucza obcego nosi nazwę, która jest połączeniem nazwy tej nawigacji z nazwą właściwości klucza podstawowego lub alternatywnego.
- Jeśli na końcu zależnym nie ma nawigacji, właściwość klucza obcego w tle jest nazywana poprzez połączenie nazwy typu jednostki głównej z nazwą właściwości klucza podstawowego lub alternatywnego.
Usuwanie kaskadowe
Zgodnie z konwencją wymagane relacje są skonfigurowane do usuwania kaskadowego. Opcjonalne relacje są skonfigurowane tak, aby nie usuwać kaskadowo.
Wiele do wielu
Relacje wiele-do-wielu nie mają końców głównych i zależnych, a żaden koniec nie zawiera właściwości klucza obcego. Zamiast tego relacje wiele-do-wielu używają typu jednostki łączącej, który zawiera pary kluczy obcych wskazujących na oba końce wiele-do-wielu. Rozważ następujące typy jednostek, dla których relacja wiele-do-wielu jest wykrywana zgodnie z konwencją:
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>();
}
Konwencje używane w tym odkryciu są:
- Typ jednostki sprzężenia ma nazwę
<left entity type name><right entity type name>. Więc,PostTagw tym przykładzie.- Tabela sprzężenia ma taką samą nazwę jak typ jednostki sprzężenia.
- Typ jednostki sprzężenia ma właściwość klucza obcego dla każdego kierunku relacji. Są one nazwane
<navigation name><principal key name>. W tym przykładzie właściwości klucza obcego toPostsIdiTagsId.- W przypadku jednokierunkowej relacji wiele-do-wielu, właściwość klucza obcego bez skojarzonej nawigacji nosi nazwę
<principal entity type name><principal key name>.
- W przypadku jednokierunkowej relacji wiele-do-wielu, właściwość klucza obcego bez skojarzonej nawigacji nosi nazwę
- Właściwości klucza obcego są niemogące mieć wartości null, co sprawia, że obie relacje z jednostką powiązania są konieczne.
- Konwencje usuwania kaskadowego oznaczają, że te relacje zostaną skonfigurowane do usuwania kaskadowego.
- Typ jednostki łączenia jest skonfigurowany z użyciem złożonego klucza podstawowego składającego się z dwóch właściwości klucza obcego. W tym przykładzie klucz podstawowy składa się z
PostsIdiTagsId.
Spowoduje to wykonanie następującego modelu 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
I tłumaczy się na następujący schemat bazy danych podczas korzystania z 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");
Indeksy
Zgodnie z konwencją program EF tworzy indeks bazy danych dla właściwości lub właściwości klucza obcego. Typ utworzonego indeksu jest określany przez:
- Kardynalność relacji
- Czy relacja jest opcjonalna, czy wymagana
- Liczba właściwości tworzących klucz obcy
W przypadku relacji jeden do wielu prosty indeks jest tworzony zgodnie z konwencją. Ten sam indeks jest tworzony dla relacji opcjonalnych i wymaganych. Na przykład w witrynie SQLite:
CREATE INDEX "IX_Post_BlogId" ON "Post" ("BlogId");
Lub w programie SQL Server:
CREATE INDEX [IX_Post_BlogId] ON [Post] ([BlogId]);
W przypadku wymaganej relacji jeden do jednego tworzony jest unikatowy indeks. Na przykład w witrynie SQLite:
CREATE UNIQUE INDEX "IX_Author_BlogId" ON "Author" ("BlogId");
Lub na serwerze SQL:
CREATE UNIQUE INDEX [IX_Author_BlogId] ON [Author] ([BlogId]);
W przypadku opcjonalnych relacji jeden do jednego indeks utworzony w sqlite jest taki sam:
CREATE UNIQUE INDEX "IX_Author_BlogId" ON "Author" ("BlogId");
Jednak w SQL Server dodawany jest filtr IS NOT NULL aby lepiej obsługiwać wartości null klucza obcego. Przykład:
CREATE UNIQUE INDEX [IX_Author_BlogId] ON [Author] ([BlogId]) WHERE [BlogId] IS NOT NULL;
W przypadku złożonych kluczy obcych tworzony jest indeks obejmujący wszystkie kolumny klucza obcego. Przykład:
CREATE INDEX "IX_Post_ContainingBlogId1_ContainingBlogId2" ON "Post" ("ContainingBlogId1", "ContainingBlogId2");
Uwaga / Notatka
Program EF nie tworzy indeksów dla właściwości, które są już objęte istniejącym ograniczeniem indeksu lub klucza podstawowego.
Jak zatrzymać tworzenie indeksów dla kluczy obcych przez program EF
Indeksy mają swoje obciążenie i, jak zapytano tutaj, nie zawsze jest wskazane tworzenie ich dla wszystkich kolumn FK. Aby to osiągnąć, ForeignKeyIndexConvention można go usunąć podczas kompilowania modelu:
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.Conventions.Remove(typeof(ForeignKeyIndexConvention));
}
W razie potrzeby indeksy można nadal jawnie tworzyć dla tych kolumn kluczy obcych, które ich potrzebują.
Nazwy ograniczeń klucza obcego
Zgodnie z konwencją ograniczenia klucza obcego mają nazwę FK_<dependent type name>_<principal type name>_<foreign key property name>. W przypadku złożonych kluczy obcych <foreign key property name> staje się listą oddzieloną podkreśleniami nazw właściwości kluczy obcych.