<p><a href="https://en.wikipedia.org/wiki/Object-relational_mapping">ORM</a> taki jak Entity Framework, mocno ułatwia nam pracę w kodzie z bazą danych (goodbye ADO.NET 🙂 ), ale nie rozwiązuje za nas problemu zaprojektowania takiej bazy. Jedną z podstawowych kwestii przed jakimi stajemy jest odpowiedź na pytanie o sposób łączenia tabel. W tym poście nie będę pisał jednak o projektowaniu baz danych, (przy założeniu, że masz pomysł na swoją bazę ;)) a o sposobie implementacji znanych wszystkim relacji w podejściu Code-First.</p>
<p>Na początek przypomnijmy sobie jakimi, w relacyjnych bazach danych, relacjami (a jakże :)) mogą być związane tabele.</p>
<p><b>Jeden do jednego</b></p>
<p>Jednemu wierszowi w pierwszej tabeli, odpowiada jeden i tylko jeden wiersz z drugiej tabeli.</p>
<p><b>Jeden do wielu</b></p>
<p>Jednemu wierszowi w pierwszej tabeli, może odpowiadać wiele wierszy z drugiej tabeli.</p>
<p><b>Wiele do wielu</b></p>
<p>Jak sama nazwa wskazuje :). Taką tabelę zwykle najkorzystniej jest podzielić, przy użyciu tzw. Tabeli łączącej na relację jeden do wielu.</p>
---
<p>Teraz zobaczmy jak zaimplementować te relacje w EF w podejściu Code-First:</p>
<h2>Relacja jeden do jednego:</h2>
<p>Załóżmy, że chcemy użyć tej relacji do połączenia podstawowych danych ucznia i jego średniej. Tabele nich nazywają się Student i AverageGrade. Jeden uczeń może mieć tylko jedną średnią ocen :).</p>
<p>W EF kolumny tabel są reprezentowane przez właściwości. Nasze klasy reprezentujące tabele i mające być połączone relacją jeden do jeden, powinny oprócz właściwych sobie kolumn zawierać wirtualną składową właściwość, reprezentującą powiązaną tabelę. Czyli w klasie Student powinna być:</p>
<pre class="language-" tabindex="0"><code class="language-">public virtual AverageGrade AverageGrade {get; set;}
</code></pre>
<p>a w klasie AverageGrade:</p>
<pre class="language-" tabindex="0"><code class="language-">public virtual Student Student {get; set;}
</code></pre>
<p>W uproszczeniu i pomijając ficzery takie jak DataAnnotations, klasy mogłyby wyglądać w ten sposób:</p>
<pre class="language-" tabindex="0"><code class="language-">public class Student
{
public int Id {get; set;}
public string Name {get; set;}
public virtual AverageGrade AverageGrade {get; set;}
}
public class AverageGrade
{
public int Id {get; set;}
public float value {get; set;}
public virtual Student Student {get; set;}
}
</code></pre>
<h2>Relacja jeden do wielu:</h2>
<p>Teraz zmieńmy sobie kontekst :). Naszymi tabelami niech będą Employer i Worker. Jeden pracodawca może zatrudniać wielu pracowników, ale jeden pracownik ma tylko jednego pracodawcę.</p>
<p>W takim wypadku klasa reprezentująca tabelę z pracownikami powinna zawierać, oprócz swoich zwykłych właściwości, publiczną wirtualną właściwość pozwalającą na przypisanie do niej implementacji interfejsu generycznego ICollection<T> gdzie T to typ przechowywanych elementów, w tym wypadku Worker:</p>
<pre class="language-" tabindex="0"><code class="language-">public virtual ICollection<Worker> Workers{get; private set;}
</code></pre>
<p>Tą kolekcję powinno się zainicjalizować w konstruktorze naszej klasy. Worker natomiast powinien, tak jak w przypadku relacji jeden do jeden, posiadać odwołanie do klasy Employer:</p>
<pre class="language-" tabindex="0"><code class="language-">public virtual Employer Employer {get; private set;}
</code></pre>
<p>Obie klasy w uproszczeniu mogłyby wyglądać tak:</p>
<pre class="language-" tabindex="0"><code class="language-">public class Employer
{
public Employer()
{
this.Workers = new HashSet<Worker>();
}
public int Id {get; set;}
public decimal NominalCapital {get; set;}
public string CompanyName {get; set;}
public virtual ICollection<Worker> Workers {get; set;}
}
public class Woker
{
public int Id {get; set;}
public string Name {get; set;}
public virtual Employer Employer {get; set;}
}
</code></pre>
<h2>Relacja wiele do wielu:</h2>
<p>Na koniec została nam powszechna, choć niewdzięczna, relacja do właściwego zastosowania której, zwykle używamy trzech tabel. Dwóch głównych i jednej tzw. tabeli łączącej. Tabela łącząca służy do rozbicia relacji, na dwie jeden do wielu. W Entity Framework tabela łącząca jest tworzona automatycznie, o ile poprawnie zaimplementujemy klasy mające odzwierciedlać relację wiele do wielu. Przyjrzyjmy się tabelom: TrainginDay i Exercise. Jeden dzień treningowy, może mieć wiele ćwiczeń, tak samo jak jedno ćwiczenie może być przypisane do wielu dni treningowych. Obie klasy reprezentujące te tabele, powinny zawierać referencje do ICollection<T> (jak w jeden do wielu). Co do utworzenia tych kolekcji, to wystarczy, że zainicjujemy tylko jedną z nich.</p>
<p>Prosta implementacja tych klas mogłaby wyglądać tak:</p>
<pre class="language-" tabindex="0"><code class="language-">public class Exercise
{
public Exercise()
{
this.TrainingDays = new HashSet<TrainingDay>();
}
public int Id {get; set;}
public string Name {get; set;}
public string Description {get; set;}
public virtual ICollection<TrainingDay> TrainingDays{get; set;}
}
public class TrainingDay
{
public int Id {get; set;}
public int IdOfPlan {get; set;}
public int IdOfDayOfWeek {get; set;}
public virtual ICollection<Exercise> Exercises{get; set;}
} </code></pre>
<p>EF utworzy za nas tabelę łączącą TrainingDayExcersise zawierającą tylko klucze główne do obu tych tabel.</p>
<p>Jeżeli chodzi o relacje między tabelami w Entity Framework, to chyba udało mi się zawrzeć tu to co jest najważniejsze. W bliskiej przyszłości napiszę co nieco o czymś co nazywa się DataAnnotations. W praktyce są to atrybuty, pozwalające nakładać na nasze kolumny min. ograniczenia, czy określać klucze.</p>