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.
En-till-många-relationer används när en enda entitet är associerad med valfritt antal andra entiteter. Till exempel kan en Blog ha många associerade Posts, men varje Post är associerad med endast en Blog.
Det här dokumentet är uppbyggt kring många exempel. Exemplen börjar med vanliga fall, som även introducerar begrepp. Senare exempel omfattar mindre vanliga typer av konfiguration. En bra metod här är att förstå de första exemplen och begreppen och sedan gå till de senare exemplen baserat på dina specifika behov. Baserat på den här metoden börjar vi med enkla "obligatoriska" och "valfria" en-till-många-relationer.
Tips/Råd
Koden för alla exempel nedan finns i OneToMany.cs.
Obligatoriskt en-till-många
// Principal (parent)
public class Blog
{
public int Id { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>(); // Collection navigation containing dependents
}
// Dependent (child)
public class Post
{
public int Id { get; set; }
public int BlogId { get; set; } // Required foreign key property
public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}
En en-till-många-relation består av:
- En eller flera primära eller alternativa nyckelegenskaper för huvudentiteten, vilket är "ett" slut av en relation. Till exempel
Blog.Id. - En eller flera främmande nyckelellement på den beroende entiteten; det är den "många"-sidan av relationen. Till exempel
Post.BlogId. - Du kan också använda en samlingsnavigering på huvudentiteten som refererar till de beroende entiteterna. Till exempel
Blog.Posts. - Valfritt, en referensnavigering på den beroende entiteten som refererar till huvudentiteten. Till exempel
Post.Blog.
Så för relationen i det här exemplet:
- Egenskapen
Post.BlogIdfrämmande nyckel är inte nullbar. Detta gör relationen "obligatorisk" eftersom varje beroende (Post) måste vara relaterade till något huvud (Blog), eftersom dess främmande nyckelegenskap måste anges till något värde. - Båda entiteterna har navigering som pekar på den relaterade entiteten eller entiteterna på andra sidan relationen.
Anmärkning
En nödvändig relation säkerställer att varje beroende entitet måste associeras med någon huvudentitet. En huvudentitet kan dock alltid finnas utan beroende entiteter. En obligatorisk relation anger alltså inte att det alltid finns minst en beroende entitet. Det finns inget sätt i EF-modellen, och inte heller något standardsätt i en relationsdatabas, för att säkerställa att ett huvudobjekt är associerat med ett visst antal beroendeobjekt. Om detta behövs måste det implementeras i programlogik (affärslogik). Mer information finns i Obligatoriska navigeringar .
Tips/Råd
En relation med två navigeringar, en från beroende till huvud och en i motsatt riktning från huvud till beroenden, kallas en dubbelriktad relation.
Den här relationen identifieras av konventionen. Det är:
-
Blogidentifieras som huvudnamn i relationen ochPostidentifieras som beroende. -
Post.BlogIdidentifieras som en främmande nyckel för den beroende som refererar till huvudenhetensBlog.Idprimära nyckel. Relationen upptäcks vid behov eftersomPost.BlogIdinte kan vara null. -
Blog.Postsidentifieras som samlingsnavigering. -
Post.Blogidentifieras som referensnavigering.
Viktigt!
När du använder C# nullbara referenstyper måste referensnavigeringen vara nullbar om den främmande nyckelns egenskap är null. Om egenskapen främmande nyckel är icke-null, kan referensnavigeringen vara nullbar eller inte. I det här fallet är både Post.BlogId och Post.Blog icke-nullbara. Konstruktionen = null!; används för att markera detta som avsiktligt för C#-kompilatorn, eftersom EF vanligtvis anger instansen Blog och den inte kan vara null för en fullständigt inläst relation. Mer information finns i artikeln Att arbeta med nullbar-referenstyper.
För de fall där navigering, främmande nyckel eller nödvändig/valfri typ av relation inte identifieras av konventionen, kan dessa saker konfigureras uttryckligen. Till exempel:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasForeignKey(e => e.BlogId)
.IsRequired();
}
I exemplet ovan börjar konfigurationen av relationerna med HasMany på huvudentitetstypen (Blog) och följer sedan detta med WithOne. Precis som med alla relationer är det exakt detsamma som att börja med beroende entitetstyp (Post) och användning HasOne följt av WithMany. Till exempel:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasOne(e => e.Blog)
.WithMany(e => e.Posts)
.HasForeignKey(e => e.BlogId)
.IsRequired();
}
Inget av dessa alternativ är bättre än det andra. båda resulterar i exakt samma konfiguration.
Tips/Råd
Det är aldrig nödvändigt att konfigurera en relation två gånger, en gång från huvudenheten och sedan igen från den beroende enheten. Dessutom fungerar det vanligtvis inte att försöka konfigurera huvud- och beroende halvor av en relation separat. Välj att konfigurera varje relation från ena änden eller den andra och skriv sedan konfigurationskoden bara en gång.
Valfritt en-till-många
// Principal (parent)
public class Blog
{
public int Id { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>(); // Collection navigation containing dependents
}
// Dependent (child)
public class Post
{
public int Id { get; set; }
public int? BlogId { get; set; } // Optional foreign key property
public Blog? Blog { get; set; } // Optional reference navigation to principal
}
Detta är samma som i tidigare exempel, förutom att egenskapen främmande nyckel och navigeringen till huvudenheten nu är nullbara. Detta gör relationen "valfri" eftersom en beroende (Post) kan finnas utan att vara relaterad till något huvudnamn (Blog).
Viktigt!
När du använder C# nullbara referenstyper måste referensnavigeringen vara nullbar om den främmande nyckelns egenskap är null. I det här fallet Post.BlogId är nullbar, så Post.Blog måste också vara null. Mer information finns i artikeln Att arbeta med nullbar-referenstyper.
Precis som tidigare identifieras den här relationen av konventionen. För de fall där navigering, främmande nyckel eller nödvändig/valfri typ av relation inte identifieras av konventionen, kan dessa saker konfigureras uttryckligen. Till exempel:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasForeignKey(e => e.BlogId)
.IsRequired(false);
}
Krävs en-till-många med skuggad främmande nyckel
// Principal (parent)
public class Blog
{
public int Id { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>(); // Collection navigation containing dependents
}
// Dependent (child)
public class Post
{
public int Id { get; set; }
public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}
I vissa fall kanske du inte vill ha en sekundärnyckelegenskap i din modell, eftersom sekundärnycklar är en detalj i hur relationen representeras i databasen, vilket inte behövs när du använder relationen på ett rent objektorienterat sätt. Men om entiteter ska serialiseras, till exempel för att skicka över en tråd, kan sekundärnyckelvärdena vara ett användbart sätt att hålla relationsinformationen intakt när entiteterna inte är i ett objektformulär. Det är därför ofta pragmatiskt att behålla sekundärnyckelegenskaper i .NET-typen för detta ändamål. Främmande nyckelattribut kan vara privata, vilket ofta är en bra kompromiss för att undvika att exponera den främmande nyckeln samtidigt som dess värde kan vara kopplat till entiteten.
Efter de föregående två exemplen tar det här exemplet bort egenskapen sekundärnyckel från den beroende entitetstypen. EF skapar därför en skuggad främmande nyckelegenskap med namnet BlogId av typen int.
En viktig punkt att notera här är att C# nullable referenstyper används, så att referensnavigeringens nullbarhet används för att avgöra om främmande nyckels egenskap är nullbar och därför om relationen är frivillig eller nödvändig. Om nullbara referenstyper inte används, är skuggsekundärnyckelegenskapen nullbar som standard, vilket gör relationen valfri. I det här fallet kan du använda IsRequired för att tvinga egenskapen för sekundärnyckeln att inte vara null och göra relationen nödvändig.
Precis som tidigare identifieras den här relationen av konventionen. För de fall där navigering, främmande nyckel eller nödvändig/valfri typ av relation inte identifieras av konventionen, kan dessa saker konfigureras uttryckligen. Till exempel:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasForeignKey("BlogId")
.IsRequired();
}
Valfritt en-till-många med skuggat främmandenyckel
// Principal (parent)
public class Blog
{
public int Id { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>(); // Collection navigation containing dependents
}
// Dependent (child)
public class Post
{
public int Id { get; set; }
public Blog? Blog { get; set; } // Optional reference navigation to principal
}
Precis som i föregående exempel har egenskapen extern nyckel tagits bort från den beroende entitetens typ. EF skapar därför en skuggad främmande nyckelegenskap med namnet BlogId av typen int?. Till skillnad från föregående exempel skapas egenskapen för den främmande nyckeln som nullbar eftersom C# nullable referenstyper används och navigeringen på den beroende entitetstypen är nullbar. Detta gör relationen valfri.
När C#-nullbara referenstyper inte används, kommer främmande nyckel-egenskapen också som standard att skapas som nullbar. Det innebär att relationer med automatiskt skapade skuggegenskaper är valfria som standard.
Precis som tidigare identifieras den här relationen av konventionen. För de fall där navigering, främmande nyckel eller nödvändig/valfri typ av relation inte identifieras av konventionen, kan dessa saker konfigureras uttryckligen. Till exempel:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasForeignKey("BlogId")
.IsRequired(false);
}
En-till-många utan navigering till huvudobjekt
// Principal (parent)
public class Blog
{
public int Id { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>(); // Collection navigation containing dependents
}
// Dependent (child)
public class Post
{
public int Id { get; set; }
public int BlogId { get; set; } // Required foreign key property
}
I det här exemplet har den främmande nyckelegenskapen introducerats på nytt, men navigeringen på den beroende entiteten har tagits bort.
Tips/Råd
En relation med endast en navigering, antingen från beroende till huvudenhet eller från huvudenhet till beroende, men inte båda, kallas en enkelriktad relation.
Precis som tidigare identifieras den här relationen av konventionen. För de fall där navigering, främmande nyckel eller nödvändig/valfri typ av relation inte identifieras av konventionen, kan dessa saker konfigureras uttryckligen. Till exempel:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne()
.HasForeignKey(e => e.BlogId)
.IsRequired();
}
Observera att anropet till WithOne inte har några argument. Det här är sättet att berätta för EF att det inte finns någon navigering från Post till Blog.
Om konfigurationen startar från entiteten utan navigering måste typen av entitet i den andra änden av relationen uttryckligen anges med hjälp av det allmänna HasOne<>() anropet. Till exempel:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasOne<Blog>()
.WithMany(e => e.Posts)
.HasForeignKey(e => e.BlogId)
.IsRequired();
}
En-till-många utan navigering till huvudobjektet och med skuggad främmande nyckel
// Principal (parent)
public class Blog
{
public int Id { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>(); // Collection navigation containing dependents
}
// Dependent (child)
public class Post
{
public int Id { get; set; }
}
Det här exemplet kombinerar två av de föregående exemplen genom att ta bort både egenskapen främmande nyckel och navigeringen på den beroende entiteten.
Den här relationen identifieras av konventionen som en valfri relation. Eftersom det inte finns något i koden som kan användas för att ange att det ska krävas, krävs en minimal konfiguration med hjälp av IsRequired för att skapa en nödvändig relation. Till exempel:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne()
.IsRequired();
}
En mer komplett konfiguration kan användas för att uttryckligen konfigurera navigerings- och utländska nyckelnamn, med ett lämpligt anrop till IsRequired() eller IsRequired(false) efter behov. Till exempel:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne()
.HasForeignKey("BlogId")
.IsRequired();
}
En-till-många utan navigering till beroende objekt
// Principal (parent)
public class Blog
{
public int Id { get; set; }
}
// Dependent (child)
public class Post
{
public int Id { get; set; }
public int BlogId { get; set; } // Required foreign key property
public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}
De föregående två exemplen hade navigeringar från huvudkontot till beroenden, men ingen navigering från det beroende till huvudkontot. För de kommande exemplen introduceras navigeringen på den beroende entiteten på nytt, medan navigeringen på huvudobjektet tas bort istället.
Precis som tidigare identifieras den här relationen av konventionen. För de fall där navigering, främmande nyckel eller nödvändig/valfri typ av relation inte identifieras av konventionen, kan dessa saker konfigureras uttryckligen. Till exempel:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasOne(e => e.Blog)
.WithMany()
.HasForeignKey(e => e.BlogId)
.IsRequired();
}
Observera igen att WithMany() anropas utan argument för att indikera att det inte finns någon navigering i den här riktningen.
Om konfigurationen startar från entiteten utan navigering måste typen av entitet i den andra änden av relationen uttryckligen anges med hjälp av det allmänna HasMany<>() anropet. Till exempel:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany<Post>()
.WithOne(e => e.Blog)
.HasForeignKey(e => e.BlogId)
.IsRequired();
}
En-till-många utan navigering
Ibland kan det vara användbart att konfigurera en relation utan navigering. En sådan relation kan bara manipuleras genom att ändra värdet för främmande nyckeln direkt.
// Principal (parent)
public class Blog
{
public int Id { get; set; }
}
// Dependent (child)
public class Post
{
public int Id { get; set; }
public int BlogId { get; set; } // Required foreign key property
}
Den här relationen identifieras inte av konventionen, eftersom det inte finns några navigeringer som anger att de två typerna är relaterade. Den kan konfigureras explicit i OnModelCreating. Till exempel:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany<Post>()
.WithOne();
}
Med den här konfigurationen Post.BlogId identifieras egenskapen fortfarande som sekundärnyckel enligt konventionen, och relationen krävs eftersom egenskapen sekundärnyckel inte är nullbar. Relationen kan bli "valfri" genom att göra egenskapen för sekundärnyckeln nullbar.
En mer fullständig explicit konfiguration av den här relationen är::
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany<Post>()
.WithOne()
.HasForeignKey(e => e.BlogId)
.IsRequired();
}
En-till-många med alternativ nyckel
I alla exempel hittills är egenskapen främmande nyckel för den beroende begränsad till primärnyckelegenskapen för huvudobjektet. Sekundärnyckeln kan i stället begränsas till en annan egenskap, som sedan blir en alternativ nyckel för huvudentitetstypen. Till exempel:
// Principal (parent)
public class Blog
{
public int Id { get; set; }
public int AlternateId { get; set; } // Alternate key as target of the Post.BlogId foreign key
public ICollection<Post> Posts { get; } = new List<Post>(); // Collection navigation containing dependents
}
// Dependent (child)
public class Post
{
public int Id { get; set; }
public int BlogId { get; set; } // Required foreign key property
public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}
Den här relationen identifieras inte av konventionen, eftersom EF alltid, enligt konventionen, skapar en relation till den primära nyckeln. Det kan konfigureras explicit i OnModelCreating, genom ett anrop till HasPrincipalKey. Till exempel:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasPrincipalKey(e => e.AlternateId);
}
HasPrincipalKey kan kombineras med andra anrop för att uttryckligen konfigurera navigationsmöjligheter, egenskaper för främmande nyckel och deras obligatoriska/valfria egenskap. Till exempel:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasPrincipalKey(e => e.AlternateId)
.HasForeignKey(e => e.BlogId)
.IsRequired();
}
En-till-många med sammansatt främmande nyckel
I alla exempel hittills bestod huvudobjektets primära eller alternativa nyckelegenskap av en enda egenskap. Primära eller alternativa nycklar kan också skapas från fler än en egenskap – dessa kallas "sammansatta nycklar". När huvudobjektet för en relation har en sammansatt nyckel måste den främmande nyckeln för den beroende enheten också vara en sammansatt nyckel med samma antal egenskaper. Till exempel:
// Principal (parent)
public class Blog
{
public int Id1 { get; set; } // Composite key part 1
public int Id2 { get; set; } // Composite key part 2
public ICollection<Post> Posts { get; } = new List<Post>(); // Collection navigation containing dependents
}
// Dependent (child)
public class Post
{
public int Id { get; set; }
public int BlogId1 { get; set; } // Required foreign key property part 1
public int BlogId2 { get; set; } // Required foreign key property part 2
public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}
Den här relationen identifieras av konventionen. Den sammansatta nyckeln måste dock konfigureras explicit::
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasKey(e => new { e.Id1, e.Id2 });
}
Viktigt!
Ett sammansatt sekundärnyckelvärde anses vara null om något av dess egenskapsvärden är null. En sammansatt utländsk nyckel med en egenskap som är null och en annan som inte är null, betraktas inte som en matchning för en primär eller alternativ nyckel med samma värden. Båda kommer att betraktas som null.
Både HasForeignKey och HasPrincipalKey kan användas för att uttryckligen ange nycklar med flera egenskaper. Till exempel:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>(
nestedBuilder =>
{
nestedBuilder.HasKey(e => new { e.Id1, e.Id2 });
nestedBuilder.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasPrincipalKey(e => new { e.Id1, e.Id2 })
.HasForeignKey(e => new { e.BlogId1, e.BlogId2 })
.IsRequired();
});
}
Tips/Råd
I koden ovan har anropen till HasKey och HasMany grupperats tillsammans i en kapslad byggare. Kapslade byggare tar bort behovet av att anropa Entity<>() flera gånger för samma entitetstyp, men är funktionellt likvärdiga med anrop Entity<>() flera gånger.
Krävs en-till-många utan kaskadborttagning
// Principal (parent)
public class Blog
{
public int Id { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>(); // Collection navigation containing dependents
}
// Dependent (child)
public class Post
{
public int Id { get; set; }
public int BlogId { get; set; } // Required foreign key property
public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}
Enligt konventionen konfigureras nödvändiga relationer till kaskadborttagning. Det innebär att när huvudkontot tas bort tas även alla dess beroenden bort, eftersom beroenden inte kan finnas i databasen utan ett huvudnamn. Det är möjligt att konfigurera EF för att utlösa ett undantag i stället för att automatiskt ta bort beroende rader som inte längre kan finnas:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.OnDelete(DeleteBehavior.Restrict);
}
Självrefererande en-till-många
I alla tidigare exempel skiljer sig huvudentitetstypen från den beroende entitetstypen. Detta behöver inte vara fallet. I typerna nedan är till exempel var och en Employee relaterad till andra Employees.
public class Employee
{
public int Id { get; set; }
public int? ManagerId { get; set; } // Optional foreign key property
public Employee? Manager { get; set; } // Optional reference navigation to principal
public ICollection<Employee> Reports { get; } = new List<Employee>(); // Collection navigation containing dependents
}
Den här relationen identifieras av konventionen. För de fall där navigering, främmande nyckel eller nödvändig/valfri typ av relation inte identifieras av konventionen, kan dessa saker konfigureras uttryckligen. Till exempel:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Employee>()
.HasOne(e => e.Manager)
.WithMany(e => e.Reports)
.HasForeignKey(e => e.ManagerId)
.IsRequired(false);
}