Konwencje są domyślnymi regułami za pomocą których Entity Framework buduje model oparty na klasach encji. W rozdziale poświęconym naszej pierwszej aplikacji (EF Core - podejście database-first) schemat bazy danych został utworzony na klasach domenowych oraz klasie kontekstowej bez żadnej dodatkowej konfiguracji ponieważ zostały wykorzystane domyślne konwencje.
W tym przykładzie rozwiniemy to zagadnienie aby efektywnie tworzyć schematy bazy danych zgodnie z podejściem code-first. W znacznym stopniu rozwiniemy nasze modele ze wspomnianego powyżej wpisu:
// Poniżej jedna z konwencji tworzenia relacji jeden-do-wielu
// Różne typy zostaną szczegółowo omówione w kolejnym wpisie
public class Company
{
public int CompanyId { get; set; }
public string BrandName { get; set; }
public string Address { get; set; }
public int SaleId { 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 IList<Company> Companies { get; set; }
}
Zanim przejdziemy do szczegółowej analizy utworzymy klasę kontekstową, dodamy migrację oraz utworzymy schemat bazy danych. Na jego bazie postaramy się zrozumieć jak działają konwencję w Entity Framework. Klasa kontekstowa przyjmuje poniższą postać:
public class ApplicationDbContext : DbContext
{
public DbSet<Company> Companies { get; set; }
public ApplicationDbContext()
{
}
// W naszej prostej aplikacji konsolowej będziemy się trzymać konwencji
// ze zdefiniowaniem connection-string w metodzie OnConfiguring
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer(@"Server=PAWEL;database=EFCoreConvenctions;Integrated Security=true");
}
}
}
Analiza Schematu
Na bazie powyższej defincji EF wygeneruje bazę danych z poniższymi tabelami:
EF Core utworzył tabele dla wszystkich właściwości DbSet zawartych w klasie kontekstowej. Dodatkowo, z uwagi na właściwości referencyjne, zostały dodane połączone encje DbSet. Dla powyższego przykładu framework utworzył dwie tabele:
Companies
Sales
Spójrzcie na poniższy zrzut ekranu w celu lepszego zrozumienia mapowania właściwości referencyjnych:
EF Core wygeneruje również odpowiednie kolumny dla wszystkich określonych właściwości. Wygenerowane nazwy kolumn odpowiadają nazwą poszczególnych właściwości. W celu wygenerowania relacji pomiędzy tabelami wykorzystywana jest właściwość referencyjna i zdefiniowana kolekcja (o stosowanych typach więcej w jednym z kolejnych wpisów):
Mapowanie typów danych
Typy danych utworzonych kolumn zależą od dostawcy bazy danych i tego w jaki sposób zmapował typy danych języka C#. Poniżej przedstawiam tabele mapowania typów danych dla SQL Servera:
Typ danych języka C#
Mapowanie do SQL Servera
int
int
string
nvarchar(Max)
decimal
decimal(18,2)
flaot
real
byte[]
varbinary(Max)
datetime
datetime
bool
bit
byte
tinyint
short
smallint
long
bigint
double
float
char
nvarchar(1) – dla EF Core 5.0
Skoro mówimy o typach danych warto również omówić typy puste, tzw. Nullable. Jeżeli taki typ zostanie zdefiniowany w naszym modelu to EF utworzy kolumnę, która będzie akceptować wartości null. Z kolei jeżeli nasza właściwość będzie typu prostego, np. int, float czy decimal zostanie utworzona kolumna typu NotNull.
Klucz główny
Tworzenie klucza głównego dla tabeli zostało zaimplementowane w ciekawy sposób. EF Core utworzy kolumne dla właściwości Id lub zawierającej w nazwie Id. Teraz widziecie dlaczego tworząc modele raz wykorzystałem CompanyId a w drugim samo Id. Warto również pamiętać, że wielkość liter nie ma tutaj znaczenia.
Spójrzmy jeszcze na mapowanie skupiając się jedynie na kluczach głównych utworzonych tabel:
Klucz obcy
W przypadku kluczy obcych wymagane jest dodatkowe skupienie związane z zaimplementowanymi konwencjami w EF Core. Kolumna klucza obcego zostanie utworzona dla każdej referencyjnej właściwości nawigacyjnej (o właściwościach nawigacyjnych powiem więcej poruszając np. zagadnienie metody Include()) zdefiniowanej w danym modelu. W tym wypadku również obowiązuje wzorzec nazewnictwa:
<Nazwa referencyjnej właściwości nawigacyjnej>Id;
<Nazwa referencyjnej właściwości nawigacyjnej><Nazwa właściwości klucza głównego>.
W naszym przykładzie została utworzona kolumna klucza obcego SalesId:
Na zakończenie wpisu kilka zdań o indeksach. EF Core tworzy domyślnie indeks klastrowany (jeden dla tabeli) na kolumnach PrimaryKey oraz indeks nieklastrowany (jedn lub więcej na danej tabeli) na kolumnach ForeignKey.
Podsumowanie
W kolejnych dwóch wpisach rozwiniemy znacząco zagadnienie konwencji. Skupimy się na tworzeniu relacji jeden-do-wielu oraz jeden-do-jednego.
Wykorzystaną wiedzę użyjemy do przygotowania schematu bazy danych, który pozwoli nam na przetestowanie różnych złożonych zapytań z wykorzystaniem metod takich jak Include(), ThenInclude() czy tzw. zapytań projekcyjnych.