Prawie zapomniałem o napisaniu kontynuacji wpisu z zeszłego tygodnia, ale na szczęście temat, który chcę dzisiaj poruszyć nie jest zbyt skomplikowany. Chodzi o relacje między tabelami oraz ich odwzorowanie w kodzie – bo omawiamy przecież podejście code-first. Omówię dzisiaj trzy istniejące typy relacji: jeden do jednego, jeden do wielu oraz wiele do wielu. Zaczynajmy!
Zakładam oczywiście, że wiesz jak skonfigurować sobie Entity Framework i potrawisz uruchomić migrację aktualizującą bazę danych. Jeśli nie, zachęcam Cię do uprzedniego zapoznania się z moim artykułem:
Entity Framework i podejście code-first w ASP.NET MVC
– pierwsze kroki
Relacja jeden-do-jednego
Zakładam, że baza posiadać ma dwie tabele: Osoby i Adresy.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class Osoba { public int Id { get; set; } public string Imie { get; set; } public string Nazwisko { get; set; } public string Pesel { get; set; } } public class Adres { public int Id { get; set; } public string Ulica { get; set; } public string Kod { get; set; } public string Miejscowosc { get; set; } } |
Relacja jeden do jednego nie jest raczej zbyt często spotykana. Tak czy inaczej warto się z nią zapoznać. Wyobraź sobie, że istnieją na świecie adresy, do których może być przypisana tylko jedna osoba, a każda z tych osób może posiadać przypisany tylko jeden adres. No więc?
Na start otwarcie bramek do klas, by widziały się wzajemnie (bramki – może mało profesjonalne, ale działa na wyobraźnię – otwieram sobie dostęp).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public class Osoba { [Key] public int Id { get; set; } public string Imie { get; set; } public string Nazwisko { get; set; } public string Pesel { get; set; } public virtual Adres Adres { get; set; } } public class Adres { [Key] public int Id { get; set; } public string Ulica { get; set; } public string Kod { get; set; } public string Miejscowosc { get; set; } public virtual Osoba Osoba { get; set; } } |
Jak wymusić teraz w tabeli Adresy powiązanie 1-1 do tabeli Osoby? Dodać klucz obcy Osoby do klucza głównego Adresy.
1 2 3 4 5 6 7 8 9 10 11 | public class Adres { [Key] [ForeignKey("Osoba")] public int Id { get; set; } public string Ulica { get; set; } public string Kod { get; set; } public string Miejscowosc { get; set; } public virtual Osoba Osoba { get; set; } } |
Sprawdzę teraz, co nam to dało. Szybki update bazy danych, wygenerowanie kodu SQL definicji bazy i oto efekt:
1 2 3 4 5 6 7 8 | CREATE TABLE [dbo].[Adres] ( [id] INT NOT NULL, [ulica] NVARCHAR (MAX) NULL, [kod] NVARCHAR (MAX) NULL, [miejscowosc] NVARCHAR (MAX) NULL, CONSTRAINT [PK_dbo.Adres] PRIMARY KEY CLUSTERED ([Id] ASC), CONSTRAINT [FK_dbo.Adres_dbo.Osobas_Id] FOREIGN KEY ([Id]) REFERENCES [dbo].[Osobas] ([Id]) ); |
Relacja jeden-do-wielu
Co jeśli jedna osoba posiada dwa adresy? A jeśli jeszcze więcej adresów?
Wystarczy mała modyfikacja klucza obcego (FK jako nowe pole) w tabeli Adresy, oraz zamiana bramki z jednorazową wartością na kolekcję wartości w tabeli Osoby.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | [Table("Osoby")] public class Osoba { [Key] public int Id { get; set; } public string Imie { get; set; } public string Nazwisko { get; set; } public string Pesel { get; set; } public virtual ICollection<Adres> Adresy { get; set; } } [Table("Adresy")] public class Adres { [Key] public int Id { get; set; } public string Ulica { get; set; } public string Kod { get; set; } public string Miejscowosc { get; set; } [ForeignKey("Osoby")] public int OsobaId { get; set; } public virtual Osoba Osoby { get; set; } } |
Zmiana nazw tabel
Jak pewnie zauważyłeś, tabele otrzymują automatycznie wygenerowane nazwy. Możesz jednak przypisać im wybrane przez siebie nazewnictwo. Szerzej o adnotacjach będę pisał w kolejnym z tej serii artykule na blogu.
Generowanie definicji SQL
Po aktualizacji bazy, definicja SQL tabeli Adresy jest następująca:
1 2 3 4 5 6 7 8 9 | CREATE TABLE [dbo].[Adresy] ( [Id] INT IDENTITY (1, 1) NOT NULL, [Ulica] NVARCHAR (MAX) NULL, [Kod] NVARCHAR (MAX) NULL, [Miejscowosc] NVARCHAR (MAX) NULL, [OsobaId] INT NOT NULL, CONSTRAINT [PK_dbo.Adresy] PRIMARY KEY CLUSTERED ([Id] ASC), CONSTRAINT [FK_dbo.Adresy_dbo.Osoby_OsobaId] FOREIGN KEY ([OsobaId]) REFERENCES [dbo].[Osoby] ([Id]) ON DELETE CASCADE ); |
Relacja wiele-do-wielu
Usuwamy klucze obce, a bramki w obu tabelach otwieramy na kolekcje zamiast wartości pojedynczych. O resztę zadba EntityFramework.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | [Table("Osoby")] public class Osoba { [Key] public int Id { get; set; } public string Imie { get; set; } public string Nazwisko { get; set; } public string Pesel { get; set; } public virtual ICollection<Adres> Adresy { get; set; } } [Table("Adresy")] public class Adres { [Key] public int Id { get; set; } public string Ulica { get; set; } public string Kod { get; set; } public string Miejscowosc { get; set; } public virtual ICollection<Osoba> Osoby { get; set; } } |
Jeśli odświeżysz eksplorator bazy danych, to zobaczysz automatycznie wygenerowaną tabelę mieszającą.
Jej definicja jest następująca:
1 2 3 4 5 6 7 | CREATE TABLE [dbo].[OsobaAdres] ( [Osoba_Id] INT NOT NULL, [Adres_Id] INT NOT NULL, CONSTRAINT [PK_dbo.OsobaAdres] PRIMARY KEY CLUSTERED ([Osoba_Id] ASC, [Adres_Id] ASC), CONSTRAINT [FK_dbo.OsobaAdres_dbo.Osoby_Osoba_Id] FOREIGN KEY ([Osoba_Id]) REFERENCES [dbo].[Osoby] ([Id]) ON DELETE CASCADE, CONSTRAINT [FK_dbo.OsobaAdres_dbo.Adresy_Adres_Id] FOREIGN KEY ([Adres_Id]) REFERENCES [dbo].[Adresy] ([Id]) ON DELETE CASCADE ); |
Podsumowanie
Tyle na dzisiaj. Za tydzień we wtorek kolejny wpis z serii EntityFramework i podejście Code-First.
Poprzedni wpis z serii: Entity Framework i podejście code-first w ASP.NET MVC – pierwsze kroki
Tagi: asp.net • code-first • Daj Się Poznać 2017 • encje • entity framework • logika aplikacji