Wprowadzenie

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: EFCore: schemat bazy danych

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: EFCore: mapowanie referencji

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): EFCore: mapowanie właściwości na kolumny

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: EFCore: klucz główny tabeli

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: EFCore: tworzenie klucza obcego

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.