Úvod do relací
Tento dokument poskytuje jednoduchý úvod k reprezentaci relací v objektových modelech a relačních databázích, včetně toho, jak EF Core mapuje mezi nimi.
Relace v objektových modelech
Relace definuje, jak spolu dvě entity vzájemně souvisejí. Když například modelujete příspěvky v blogu, každý příspěvek souvisí s blogem, na který se publikuje, a blog se vztahuje ke všem příspěvkům publikovaným na tomto blogu.
V objektově orientovaném jazyce, jako je C#, jsou blog a příspěvek obvykle reprezentovány dvěma třídami: Blog
a Post
. Příklad:
public class Blog
{
public string Name { get; set; }
public virtual Uri SiteUri { get; set; }
}
public class Post
{
public string Title { get; set; }
public string Content { get; set; }
public DateTime PublishedOn { get; set; }
public bool Archived { get; set; }
}
Ve výše uvedených třídách není nic naznačovat, že Blog
a Post
jsou související. Můžete ho přidat do objektového modelu přidáním odkazu z Post
publikovaného objektového Blog
modelu:
public class Post
{
public string Title { get; set; }
public string Content { get; set; }
public DateOnly PublishedOn { get; set; }
public bool Archived { get; set; }
public Blog Blog { get; set; }
}
Stejně tak může být opačný směr stejné relace reprezentován jako kolekce Post
objektů na každém Blog
:
public class Blog
{
public string Name { get; set; }
public virtual Uri SiteUri { get; set; }
public ICollection<Post> Posts { get; }
}
Toto připojení z Blog
Post
a inverzního připojení Post
z pozadí do Blog
se v EF Core označuje jako "relace".
Důležité
Jedna relace se obvykle prochází v obou směrech. V tomto příkladu je to od Blog
do Post
vlastnosti Blog.Posts
a zpět Post
přes Blog
Post.Blog
vlastnost. Toto je jedna relace, ne dvě.
Tip
V EF Core se vlastnosti Blog.Posts
označují Post.Blog
jako "navigace".
Relace v relačních databázích
Relační databáze představují relace pomocí cizích klíčů. Například pomocí SQL Serveru nebo Azure SQL můžete k reprezentaci našich Post
a Blog
tříd použít následující tabulky:
CREATE TABLE [Posts] (
[Id] int NOT NULL IDENTITY,
[Title] nvarchar(max) NULL,
[Content] nvarchar(max) NULL,
[PublishedOn] datetime2 NOT NULL,
[Archived] bit NOT NULL,
[BlogId] int NOT NULL,
CONSTRAINT [PK_Posts] PRIMARY KEY ([Id]),
CONSTRAINT [FK_Posts_Blogs_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [Blogs] ([Id]) ON DELETE CASCADE);
CREATE TABLE [Blogs] (
[Id] int NOT NULL IDENTITY,
[Name] nvarchar(max) NULL,
[SiteUri] nvarchar(max) NULL,
CONSTRAINT [PK_Blogs] PRIMARY KEY ([Id]));
V tomto relačním modelu Posts
jsou tabulky a Blogs
tabulky uvedeny ve sloupci "primární klíč". Hodnota primárního klíče jednoznačně identifikuje každý příspěvek nebo blog. Tabulka má navíc Posts
sloupec "cizí klíč". Na Blogs
sloupec primárního BlogId
klíče odkazuje sloupec cizího Posts
klíče tabulky.Id
Tento sloupec je "omezený", aby jakákoli hodnota ve BlogId
sloupci Posts
musí odpovídat hodnotě Id
ve sloupci Blogs
. Tato shoda určuje, ke kterému příspěvku se každý příspěvek vztahuje. Pokud BlogId
je například hodnota v jednom řádku Posts
tabulky 7, pak se příspěvek reprezentovaný tímto řádkem publikuje na blogu s primárním klíčem 7.
Mapování relací v EF Core
Mapování relací EF Core se týká mapování reprezentace primárního klíče nebo cizího klíče použitého v relační databázi na odkazy mezi objekty použitými v objektovém modelu.
V nejzásadnějším smyslu to zahrnuje:
- Přidání vlastnosti primárního klíče ke každému typu entity
- Přidání vlastnosti cizího klíče do jednoho typu entity
- Přidružení odkazů mezi typy entit k primárním a cizím klíčům pro vytvoření jedné konfigurace relace.
Po provedení tohoto mapování ef změní hodnoty cizího klíče podle potřeby, když se změní odkazy mezi objekty a změní odkazy mezi objekty podle potřeby při změně hodnot cizího klíče.
Poznámka:
Primární klíče se používají pro více než mapování relací. Další informace najdete v tématu Klíče .
Například výše uvedené typy entit lze aktualizovat pomocí vlastností primárního a cizího klíče:
public class Blog
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Uri SiteUri { get; set; }
public ICollection<Post> Posts { get; }
}
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime PublishedOn { get; set; }
public bool Archived { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
Tip
Vlastnosti primárního a cizího klíče nemusí být veřejně viditelné vlastnosti typu entity. I když jsou však vlastnosti skryté, je důležité si uvědomit, že v modelu EF stále existují.
Vlastnost primárního Blog
klíče , Blog.Id
a cizí Post
klíč vlastnost , Post.BlogId
pak může být přidružena k odkazům ("navigace") mezi typy entit (Blog.Posts
a Post.Blog
). Ef to provádí automaticky při vytváření jednoduché relace, jako je tato, ale může být také zadán explicitně při přepsání OnModelCreating
metody vašeho DbContext
. Příklad:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasForeignKey(e => e.BlogId)
.HasPrincipalKey(e => e.Id);
}
Nyní se všechny tyto vlastnosti budou chovat koherentně společně jako reprezentace jednoho vztahu mezi Blog
a Post
.
Další informace
EF podporuje mnoho různých typů relací s mnoha různými způsoby, jak lze tyto relace reprezentovat a konfigurovat. Pokud chcete přejít na příklady pro různé druhy relací, přečtěte si téma:
- Relace 1:N, ve kterých je jedna entita přidružená k libovolnému počtu dalších entit.
- Relace 1:1, ve kterých je jedna entita přidružená k jiné jedné entitě.
- Relace M:N, ve kterých je libovolný počet entit přidružených k libovolnému počtu dalších entit.
Pokud s EF teprve začínáte, vyzkoušejte příklady propojené v bodech odrážek výše dobrým způsobem, jak získat pocit, jak vztahy fungují.
Podrobnější informace o vlastnostech typů entit zahrnutých v mapování relací najdete tady:
- Cizí a hlavní klíče v relacích, které se týkají mapování cizích klíčů na databázi.
- Navigace relací, které popisují, jak jsou navigace vrstvené přes cizí klíč, aby poskytovaly objektově orientované zobrazení relace.
Modely EF se vytvářejí pomocí kombinace tří mechanismů: konvence, atributy mapování a rozhraní API pro tvůrce modelů. Většina příkladů ukazuje rozhraní API pro vytváření modelů. Další informace o dalších možnostech najdete tady:
- Konvence relací, které zjišťují typy entit, jejich vlastnosti a vztahy mezi těmito typy.
- Atributy mapování relací, které je možné použít jako alternativu k rozhraní API pro vytváření modelu pro některé aspekty konfigurace vztahu.
Důležité
Rozhraní API pro vytváření modelů je konečným zdrojem pravdy pro model EF – vždy má přednost před konfigurací zjištěnou konvencí nebo určenými atributy mapování. Je to také jediný mechanismus s plnou věrností ke konfiguraci všech aspektů modelu EF.
Mezi další témata související s relacemi patří:
- Kaskádové odstranění, které popisují, jak se související entity dají automaticky odstranit, když
SaveChanges
je volána neboSaveChangesAsync
volána. - Typy vlastněných entit používají speciální typ relace "vlastnící", která znamená silnější propojení mezi těmito dvěma typy než "normální" relace, které jsou zde popsány. Mnoho konceptů popsaných zde pro normální relace se přenáší do vlastněných relací. Vlastní relace ale mají také vlastní zvláštní chování.
Tip
Při čtení dokumentace si projděte glosář termínů relací, které vám pomůžou porozumět použité terminologii.
Použití relací
Relace definované v modelu se dají použít různými způsoby. Příklad:
- Relace se dají použít k dotazování souvisejících dat jedním ze tří způsobů:
- Dychtivě jako součást dotazu LINQ pomocí
Include
. - Lazily s využitím opožděných proxy serverů nebo opožděného načítání bez proxy serverů.
- Explicitní použití
Load
metod neboLoadAsync
metod
- Dychtivě jako součást dotazu LINQ pomocí
- Relace lze použít v počátečních datech prostřednictvím párování hodnot PK s hodnotami FK.
- Relace se dají použít ke sledování grafů entit. Relace se pak pomocí sledování změn používají k:
- Detekce změn v relacích a provádění oprav
- Odesílání aktualizací cizího klíče do databáze pomocí
SaveChanges
neboSaveChangesAsync