W poprzednim rozdziale poruszyliśmy temat konwencji stosowanych w EF, które mapują poszczególne właściwości na odpowiednie obiekty (tabele, kolumny) po stronie bazy danych. W tym wpisie poruszymy temat konwencji definiujących relacje pomiędzy tabelami skupiając się na relacji jeden do wielu.
W tym wpisie wykorzystamy nasz domyślny model utworzony w jednym z pierwszych wpisów. Na jego bazie przyjrzymy się różnym konwencjom, które automatycznie wygenerują relację jeden do wielu pomiędzy przygotowanymi modelami:
public class Company
{
public int CompanyId { get; set; }
public string BrandName { get; set; }
}
public class Sales
{
public int Id { get; set; }
public int Quantity { get; set; }
public decimal Summary { get; set; }
}
Konwencja 1
W pierwszym przypadku zbudujemy relację jeden do wielu w której wiele marek związanych jest z konkretną sprzedażą. Przykład może nie jest najlepszy – tutaj jednak nacisk kładę na różne konwencje. Relacje takie możemy zbudować poprzez dodawanie referencyjnej właściwości nawigacji w jednostce zależnej (Company):
public class Company
{
public int CompanyId { get; set; }
public string BrandName { get; set; }
public Sales Sales {get;set;}
}
public class Sales
{
public int Id { get; set; }
public int Quantity { get; set; }
public decimal Summary { get; set; }
}
W powyższym przykładzie klasa encji Company zawiera referencyjną właściwość nawigacyjną typu Sales. Taka konstrukcja pozwala nam na powiązanie tych samych wartości sprzedaży z różnymi firmami co tworzy relację jeden do wielu. Po stronie bazy danych tabela Company będzie zawierała defincje klucza obcego SalesId, który będzie zdefiniowany jako nullable. Spójrzie jak wygląda schemat bazy danych:
Tak jak wspomniałem powyżej, właściwość referencyjna SalesId jest kluczem obcym zdefiniowanym jako typ dopuszczający wartości puste. Jeżeli chcielibyśmy zmienić typ na not null musilibyśmy posłużyć się konfiguracją z wykorzystaniem Fluent API.
Udało nam się zdefiniować relację jeden do wielu? Tak, możemy określić (hipotetyczną sprzedaż) na poziomie 1000. Wartość ta, poprzez klucz obcy SalesId, może zostać przypisana do różnych firm.
Konwencja 2
Tym razem nieco inne podejście. Do encji głównej dodajmy tzw. właściwość nawigacji zbiorczej jak możecie zobaczyć poniżej:
public class Companies
{
public int CompanyId { get; set; }
public string BrandName { get; set; }
}
public class Sales
{
public int Id { get; set; }
public int Quantity { get; set; }
public decimal Summary { get; set; }
public ICollection<Companies> Companies { get; set; }
}
Rezultatem dodania migracji oraz utworzenia bazy danych jest ten sam schemat jak w przypadku pierwszej konwencji:
W powyższym przypadku również udało nam się zbudować relację jeden do wielu – tym razem przy wykorzystaniu właściwości nawigacyjnej typu ICollection.
Konwencja 3
Kolejny sposób to dodanie właściwości referencyjnych w obu modelach. Jest to połączeniem dwóch powyższych sposobów:
public class Companies
{
public int CompanyId { get; set; }
public string BrandName { get; set; }
public Sales Sales { get;set; }
}
public class Sales
{
public int Id { get; set; }
public int Quantity { get; set; }
public decimal Summary { get; set; }
public ICollection<Companies> Companies { get; set; }
}
Encja Companies zawiera referencyjną właściwość nawigacyjną Sales. Encja Sales zawiera właściwość nawigacyjną typu ICollection. Efektem zastosowania takiej konwencji jest również zbudowanie relacji jeden do wielu:
Konwencja 4
Kolejny sposób to tzw. pełna definicja relacji z kluczem obcym w encji zależnej:
public class Companies
{
public int CompanyId { get; set; }
public string BrandName { get; set; }
public int SalesId { get; set; }
public Sales Sales { get; set; }
}
public class Sales
{
public int Id { get; set; }
public int Quantity { get; set; }
public decimal Summary { get; set; }
public ICollection<Companies> Companies { get; set; }
}
W powyższym przykładzie encja Company zawiera klucz obcy SalesId typu int oraz odpowiadającą nawigacyjną właściwość referencyjną Sales. W drugiej encji zdefiniowaliśmy właściwość referencjną typu ICollection. Takie podejście również pozwala na utworzenie realacji jeden do wielu:
Zwróćcie uwagę, że w tym przypadku jawnie zdefiniowaliśmy typ dla klucza obcego w postaci int. Tym razem nasz klucz obcy jest typu not null. Jeżeli taka defincja nam nie odpowiada możemy skorzystać z typu Nullable dostępnego w C# lub zapisu skróconego int?.
Podsumowanie
W tym wpisie omówiliśmy konwencje, które automatycznie tworzą relację jeden do wielu pomiędzy odpowiednimi tabelami bazy danych. Jeżeli encje nie przestrzegają powyższych konwencji możemy użyć Fluent API w celu przygotowania odpowiedniej konfiguracji dla relacji jeden do wielu – o tym już niebawem.