Relacje: jeden-do-wielu i wiele-do-wielu
Modeluj i odpytuj relacje jeden-do-wielu oraz wiele-do-wielu za pomocą właściwości nawigacyjnych i Fluent API.
Relacje: jeden-do-wielu i wiele-do-wielu to bezpłatna lekcja C# Academy na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej C# Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs C# Academy zawiera 4 lekcji w sumie.
Relacje w EF Core
EF Core modeluje trzy typy relacji między encjami: jeden do jednego, jeden do wielu i wiele do wielu. Każdy typ wyraża się za pomocą właściwości nawigacyjnych i można go opcjonalnie skonfigurować za pomocą Fluent API.
Jeden do wielu: konfiguracja encji
Blog ma wiele elementów Posts. Encja zależna (Post) przechowuje klucz obcy. Właściwości nawigacyjne po obu stronach umożliwiają poruszanie się po relacji w obu kierunkach.
public class Blog
{
public int Id { get; set; }
public string Name { get; set; } = "";
public ICollection<Post> Posts { get; set; } = new List<Post>();
}
public class Post
{
public int Id { get; set; }
public string Title { get; set; } = "";
public int BlogId { get; set; } // FK
public Blog Blog { get; set; } = null!; // nav property
}Fluent API: relacja jeden do wielu
EF Core rozpoznaje relację na podstawie konwencji, ale można ją jawnie skonfigurować za pomocą Fluent API, aby zwiększyć czytelność i ustawić zachowanie podczas kaskadowego usuwania.
protected override void OnModelCreating(ModelBuilder mb)
{
mb.Entity<Blog>()
.HasMany(b => b.Posts)
.WithOne(p => p.Blog)
.HasForeignKey(p => p.BlogId)
.OnDelete(DeleteBehavior.Cascade);
}Wykonywanie zapytań za pomocą Include (ładowanie eager)
Należy użyć Include, aby załadować powiązane dane w ramach tego samego zapytania. Za pomocą ThenInclude można przejść głębiej w grafie nawigacji.
// Load blog with all its posts
var blog = await _db.Blogs
.Include(b => b.Posts)
.FirstOrDefaultAsync(b => b.Id == 1);
// Load posts with their comments too
var blogs = await _db.Blogs
.Include(b => b.Posts)
.ThenInclude(p => p.Comments)
.ToListAsync();Relacja wiele do wielu bez encji łączącej
EF Core 5+ obsługuje relacje wiele do wielu bez jawnej encji łączącej. Automatycznie tworzy tabelę łączącą na podstawie nazw typów encji.
public class Student
{
public int Id { get; set; }
public string Name { get; set; } = "";
public ICollection<Course> Courses { get; set; } = new List<Course>();
}
public class Course
{
public int Id { get; set; }
public string Title { get; set; } = "";
public ICollection<Student> Students { get; set; } = new List<Student>();
}
// EF creates StudentCourse join table automaticallyRelacja wiele do wielu z encją łączącą
Gdy tabela łącząca wymaga dodatkowych kolumn (np. daty zapisania i oceny), należy utworzyć jawną encję łączącą ze złożonym kluczem głównym.
public class Enrollment
{
public int StudentId { get; set; }
public Student Student { get; set; } = null!;
public int CourseId { get; set; }
public Course Course { get; set; } = null!;
public DateTime EnrolledAt { get; set; }
public decimal? Grade { get; set; }
}
// Fluent API config:
mb.Entity<Enrollment>().HasKey(e => new { e.StudentId, e.CourseId });
mb.Entity<Enrollment>().HasOne(e => e.Student).WithMany(s => s.Enrollments);
mb.Entity<Enrollment>().HasOne(e => e.Course).WithMany(c => c.Enrollments);Wstawianie powiązanych danych
Należy dodać powiązane encje do kolekcji encji nadrzędnej przed wywołaniem SaveChangesAsync. EF Core automatycznie obsłuży wartości FK.
var blog = new Blog { Name = "Dev Notes" };
blog.Posts.Add(new Post { Title = "Hello EF Core" });
blog.Posts.Add(new Post { Title = "Migrations Deep Dive" });
_db.Blogs.Add(blog);
await _db.SaveChangesAsync();
// Inserts Blog + 2 Posts with correct BlogId FKFiltrowanie powiązanych danych
W wyrażeniach LINQ można filtrować dane za pomocą właściwości nawigacyjnych. EF Core automatycznie tłumaczy takie zapytania na klauzule SQL JOIN.
// Blogs that have at least one published post
var popularBlogs = await _db.Blogs
.Where(b => b.Posts.Any(p => p.IsPublished))
.Select(b => new { b.Name, PostCount = b.Posts.Count(p => p.IsPublished) })
.OrderByDescending(b => b.PostCount)
.ToListAsync();Kaskadowe usuwanie
W przypadku użycia DeleteBehavior.Cascade usunięcie encji głównej (Blog) automatycznie usuwa wszystkie encje zależne (Posts). Inne opcje to: SetNull, Restrict, NoAction.
// Delete a blog and all its posts
var blog = await _db.Blogs.FindAsync(1);
_db.Blogs.Remove(blog!);
await _db.SaveChangesAsync();
// Cascade: all Posts with BlogId=1 are also deletedTypy encji należących jako obiekty wartościowe
Typy encji należących doskonale nadają się do modelowania obiektów wartościowych (np. Address). Są przechowywane w tej samej tabeli co encja właściciela i nie mają niezależnej tożsamości.
public class Customer
{
public int Id { get; set; }
public string Name { get; set; } = "";
public Address ShippingAddress { get; set; } = new();
}
public class Address
{
public string Street { get; set; } = "";
public string City { get; set; } = "";
public string Country { get; set; } = "";
}
// Fluent API:
mb.Entity<Customer>().OwnsOne(c => c.ShippingAddress);Praktyczny przykład: zamówienie z elementami
Kompletny wzorzec e-commerce: Order (encja główna) z wieloma OrderLines (encjami zależnymi), z których każda odwołuje się do Product.
var order = await _db.Orders
.Include(o => o.Lines)
.ThenInclude(l => l.Product)
.FirstOrDefaultAsync(o => o.Id == orderId);
if (order is null) return Results.NotFound();
var summary = new
{
OrderId = order.Id,
Total = order.Lines.Sum(l => l.Quantity * l.Product.Price),
Items = order.Lines.Select(l => new
{
l.Product.Name,
l.Quantity,
LineTotal = l.Quantity * l.Product.Price
})
};
return Results.Ok(summary);Szybkie sprawdzenie
Kiedy EF Core automatycznie tworzy tabelę łączącą bez jawnej encji łączącej?
Podsumowanie: relacje w EF Core
Najważniejsze informacje:
- Relacja jeden do wielu: FK znajduje się w encji zależnej, właściwości nawigacyjne są po obu stronach; do ładowania należy użyć Include
- Relacja wiele do wielu (prosta): dwie właściwości nawigacyjne ICollection — EF Core automatycznie tworzy tabelę łączącą
- Relacja wiele do wielu (z danymi dodatkowymi): jawna encja łącząca ze złożonym kluczem PK
- Kaskadowe usuwanie określa, co dzieje się z encjami zależnymi po usunięciu encji głównej
- Encje należące służą do modelowania obiektów wartościowych w tej samej tabeli co encja właściciela
Często zadawane pytania
Czy lekcja „Relacje: jeden-do-wielu i wiele-do-wielu” jest bezpłatna?
Tak — pełny tekst „Relacje: jeden-do-wielu i wiele-do-wielu” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu C# Academy, przejdź na CoddyKit PRO. Kurs C# Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Relacje: jeden-do-wielu i wiele-do-wielu”?
Modeluj i odpytuj relacje jeden-do-wielu oraz wiele-do-wielu za pomocą właściwości nawigacyjnych i Fluent API. Ćwiczysz C# Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć C# Academy?
Nie wymagamy żadnego doświadczenia. C# Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.
Ile czasu zajmuje lekcja „Relacje: jeden-do-wielu i wiele-do-wielu”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji C# Academy?
Tak. Każda lekcja C# Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Podstawy DbContext i DbSet
- Migracje i zarządzanie schematem
- Operacje CRUD z EF Core
- Relacje: jeden-do-wielu i wiele-do-wielu